Comments below marked by ###
--
Mac :})
** I normally forward private questions to the appropriate mail list. **
Ask Smarter: http://www.tuxedo.org/~esr/faqs/smart-questions.html
Give a hobbit a fish and he eats fish for a day.
Give a hobbit a ring and he eats fish for an age.
----- Original Message -----
From: "Shao, Chunning" <[EMAIL PROTECTED]>
To: "Michael A Chase" <[EMAIL PROTECTED]>
Cc: <[EMAIL PROTECTED]>
Sent: Monday, March 04, 2002 12:04
Subject: RE: Error in explain plan


Hi, Mike,

adding $sql_result =~ s/:(\d+)/:p$1/g;  to the code did solve the
problem, but change $dbh->state to $dbh->err does not help without
adding the line.

The only concern I have here is by such change, it might go through a
defferent path than the real one

Thanks you very much.

The following is my code.  Let me know if you have any more comments,
that is really helpful.


#!/usr/local/bin/perl -w
########################################################################
#########
# Purpose : Explain plan for all the script in the database
# Created : 20020208
# Author  :
########################################################################
#########

sub trap_die
{
        my @param = @_;
        $message = $param[0];
$message_body = $param[1];
        $CommandString = "/bin/echo  \"$message_body\" | /bin/mailx -s
\"$message\" cshao\@traffic.com ";
        system ( $CommandString );
}

sub print_date
{    use Time::localtime;
       my $tm = localtime;
       printf("at time  %04d-%02d-%02d %02d:%02d:%02d\n",
         $tm->year+1900,
         ($tm->mon)+1,$tm->mday,$tm->hour,$tm->min,$tm->sec);
}

my $i = 0;
my @instance = ('dev');
my @username = ('sys');
my @passwd = ('xxxx');


my
($dbh,$addr,$hashval,$parse_user,$sth,@row,$stmt_id,$sql_text,$error);
my ($sql,$csr,$data,$userdata ,$rv,$maildata,$message_body,$message );
my ($sql_explain,$sql_result,$errorstat,$length);
my ($failure,$counter);
while ($i <= $#instance ) {
use strict;
use DBI;

### Thes 'use' directives don't need to be inside the loop.  It would
probably be better to put them at the top of your program.

eval {
 $dbh = DBI->connect ( "dbi:Oracle:$instance[$i]",
                        "$username[$i]",
                        "$passwd[$i]",
                        {
                                PrintError => 0,
                                RaiseError => 0,
                                AutoCommit => 1
                        }
                        ) || die "Database Connection not made
$DBI::errstr" ;# Create a Database

### With RaiseError disabled, the only way you can die() at this point is if
the DBD::Oracle driver is unavailable.

### If you are disabling RaiseError, you need to check each method call for
success or check $DBI::err after each.

#check to see whether system.sqltemp exist
$rv = &getValue("select count(*) from dba_tables where table_name =
'SQLTEMP' and owner = 'SYSTEM'");
if( $rv == 0)
                        {
                         $sql = "create table sqltemp
                                (
                                   ADDR        VARCHAR2 (8)
                                  ,HASHVAL     INTEGER
                                  ,SQL_TEXT    VARCHAR2(2000)
                                  ,DISK_READS  NUMBER
                                  ,EXECUTIONS  NUMBER
                                  ,PARSE_CALLS NUMBER
                                  ,PARSE_USER  VARCHAR2(30)
                                  ,STMT_ID     VARCHAR2(100)
                                ) tablespace users";
                        $dbh->do($sql);
print "table system.sqltemp crated\n";

### False, you just created sys.sqltemp, not system.sqltemp.

                        }

#check to see whether system.plan_table exist
$rv = &getValue("select count(*) from dba_tables where table_name =
'PLAN_TABLE' and owner = 'SYSTEM'");
if( $rv == 0)
                        {
                         $sql = "create table PLAN_TABLE (
                                statement_id    varchar2(30),
                                timestamp       date,
                                remarks         varchar2(80),
                                operation       varchar2(30),
                                options         varchar2(30),
                                object_node     varchar2(128),
                                object_owner    varchar2(30),
                                object_name     varchar2(30),
                                object_instance numeric,
                                object_type     varchar2(30),
                                optimizer       varchar2(255),
                                search_columns  number,
                                id              numeric,
                                parent_id       numeric,
                                position        numeric,
                                cost            numeric,
                                cardinality     numeric,
                                bytes           numeric,
                                other_tag       varchar2(255),
                                partition_start varchar2(255),
                                partition_stop  varchar2(255),
                                partition_id    numeric,
  other           long,
                                distribution    varchar2(30))tablespace
users";
                                $dbh->do($sql);
                                print "table system.plan_table
crated\n";


### False, you just created sys.plan_table, not system.plan_table.

                        } #end of if

#truncate table system.plan_table and system.sqltemp
$failure = 1;
$counter = 0;

while (( $failure != 0 )  && ( $counter < 50))
{
$sql = "truncate table system.plan_table";
$dbh -> do($sql);
$rv = &getValue("select count(*) from system.plan_table");
if ($rv > 0) {$failure = 1;$counter = $counter + 1;}
else {$failure = 0;}
}
if ($counter ==50)
{
$sth->finish();
        $dbh->disconnect();
        trap_die("$instance[$i].Explain_plan failed","Error dropping
system.plan_table\n");
}

### There is no reason to attempt TRUNCATEing multiple times.  If it fails
once, it will fail 50 times.

#truncate system.sqltemp table
$failure = 1;
$counter = 0;

while (( $failure != 0 )  && ( $counter < 50 ))
{
$sql = "truncate table system.sqltemp";
$dbh -> do($sql);
$rv = &getValue("select count(*) from system.sqltemp");
        if ($rv > 0) {$failure = 1;$counter = $counter + 1;}
        else    {$failure = 0;}
}
if ($counter ==50)
{
        $sth->finish();
        $dbh->disconnect();
        trap_die("$instance[$i].Explain_plan failed","Error dropping
system.sqltemp\n");
}

### There is no reason to attempt TRUNCATEing multiple times.  If it fails
once, it will fail 50 times.

#populate the sqltemp table
$sql = " INSERT INTO system.sqltemp
        SELECT  RAWTOHEX(SA.address) addr
         ,SA.hash_value        hash
         ,SA.sql_text          sql_text
         ,SA.DISK_READS        diskrds
         ,SA.EXECUTIONS        execs
         ,SA.PARSE_CALLS       parses
         ,DU.username          username
         ,SUBSTR(RAWTOHEX(SA.address)||':'||TO_CHAR(SA.hash_value) ,
1,30) stmt_id
        FROM  v\$sqlarea   SA,DBA_USERS   DU
        WHERE
                      command_type in (2,3,6,7)
      AND
          SA.parsing_schema_id != 0
      AND SA.parsing_schema_id = DU.user_id
      AND DU.username not like '%SYS%'
      AND DU.username <> 'QDBA'
      AND DU.username <> 'PERFSTAT'";
$dbh -> do($sql);



# get the username etc from system.sqltemp
$sql = "SELECT
addr,hashval,parse_user,stmt_id,sql_text,length(sql_text)  from
system.sqltemp where  parse_user not like '%SYS%' ";

$csr = $dbh->prepare($sql);
$csr->execute();
$userdata = $csr->fetchall_arrayref();
foreach   $data (@$userdata)
{
$addr = $data->[0];
$hashval = $data->[1];
$parse_user = $data->[2];
$stmt_id = $data->[3];
$length = $data->[5];
                $sql_result = "";

$sql = "alter session set current_schema=$parse_user";
$dbh->do($sql);
#get the sql statement
if ($length > 990)
{
$sql_explain = "select sql_text from v\$sqltext
where address = hextoraw('$addr') and hash_value = $hashval  order by
piece";
$sth = $dbh->prepare($sql_explain);
$sth->execute();

### With placeholders for $addr and $hashval, you should only need to
prepare this statement once and then execute it with the appropriate values
inside the loop.

while ( my @row = $sth->fetchrow_array )
{
$sql_text = $row[0];
#print "$sql_text\n";
$sql_result = $sql_result.$sql_text;
}
}
else
{
$sql_result = $data->[4];
}
$sql_result =~ s/:(\d+)/:p$1/g;

$failure = 1;
$counter = 0;

while (( $failure != 0 )  && ( $counter < 4 ))
{
        $sql_explain = "explain plan set statement_id = '$stmt_id' into
system.plan_table for $sql_result";
$dbh->do($sql_explain);

### Please read the sections in 'perldoc DBI' that explain what 'state',
'err', and 'errstr' are.  I suspect that all you need to check is
$DBI::errstr and that would be much more meaningful in your error messages.

#remove the end line from Java if explain failed
chop ($sql_result);
if (($dbh->state ne "") && ($dbh->errstr eq ""))
        {
        $failure = 1;
        $counter = $counter + 1;
        print "DO_explain failed",$dbh->state,"$stmt_id\n";
$error= $dbh->state;
        }
elsif (($dbh->state ne "") && ( $dbh->errstr ne ""))
{
$errorstat =  $dbh->state;
$error = $dbh->errstr;
        $error = substr ($error,0,30);
        $sql = "INSERT INTO system.plan_table(statement_id,remarks)
VALUES ('$stmt_id', '$error')";
$dbh->do($sql);

### It would be much more efficient to use placeholders for the values.

$failure = 0;
print "insert error $stmt_id\n";
}
else    {
        $failure = 0;
print "succeed no hassle $stmt_id\n";
        }
}  #end of failure check

}  #end of the userdata while

#check to see how many rows is not explained
$rv = &getValue("select count(*) from system.sqltemp where stmt_id not
in (
select statement_id from system.plan_table)");
if ($rv > 0)
{
# get parser_user and stmt_id
        $sql = "SELECT parse_user,stmt_id from system.sqltemp
where stmt_id not in ( select statement_id from
system.plan_table) ";

$message = substr("For $instance[$i]",0,8)."\n";
$message_body = $message_body.$message;

        $csr = $dbh->prepare($sql);
        $csr->execute();
        $maildata = $csr->fetchall_arrayref();
                foreach   $data (@$maildata)
                {
                $parse_user = $data->[0];
                $stmt_id = $data->[1];
                $message = sprintf("%10s
%20s\n",$parse_user,$stmt_id);
                $message_body = $message_body.$message;
                }  #end of the userdata while
$message = "is not explained\n";
        $message_body = $message_body.$message;
}

#get parse user,stmt_id which is explained ERROR
$rv = &getValue("select count(*) from system.plan_table
                where remarks is not null");
if ($rv > 0)
        {
        # get parser_user and stmt_id

$message = substr("For $instance[$i]",0,8)."\n";
        $message_body = $message_body.$message;

        $sql = "SELECT parse_user,stmt_id from system.sqltemp where
stmt_id in (select statement_id from system.plan_table
                where remarks is not null)";


        $csr = $dbh->prepare($sql);
        $csr->execute();
        $maildata = $csr->fetchall_arrayref();
                foreach   $data (@$maildata)
                {
                $parse_user = $data->[0];
                $stmt_id = $data->[1];

                $message = sprintf("%10s
%20s\n",$parse_user,$stmt_id);
                $message_body = $message_body.$message;
                }  #end of the userdata while
        $message = "is explained ERROR\n";
        $message_body = $message_body.$message;
        }

print "$message_body\n";

# Close Database Handle
$csr->finish();
$dbh->disconnect();

};   # end of eval
if ($@) {
        $dbh->disconnect();
        print "$@\n";
          }

$i++;
}   # end of while loop

if (defined $message_body)
{
trap_die ("Explain Report","$message_body");
}

### Look at selectrow_array().  It performs the same function and covers
some possible problems better.

#subroutine to get the value
sub getValue {
                my @para = @_;
                my $sql_value = $para[0];
                my $csr_sub =  $dbh->prepare($sql_value);
                $csr_sub->execute();
                return $csr_sub->fetchrow_arrayref()->[0];
                $dbh->finish();
                }  ## end of subfunction


Reply via email to