I need to query a group of SAP sybase databases for some information and print that as a comma spearated list. So I figure I write a perl script that connects to any of those databases via DBI module. Here is what I came up with.
my $user = "someuser";
my $passwd = "somepassword";
my #sids=(filled with DB identifiers);
my $output="";
my $size;
my $version;
my $id;
my $dsn;
my $dbh;
my $sid;
my #row;
my $sth1;
my $sth2;
foreach $sid (#sids) {
print $sid."\n";
$dsn = "dbi:Sybase:server=$sid;charset=iso_1;tdsLevel=CS_TDS_50";
print $dsn."\n";
$dbh = DBI->connect($dsn, $user, $passwd,{ PrintError => 0,RaiseError => 0, AutoCommit => 1, syb_enable_utf8 => 1});
print "DBI OK\n" if defined ($dbh);
$sth1 = $dbh->prepare('select SUM(size) from master..sysusages WHERE dbid = 4 AND segmap = 3');
$sth2 = $dbh->prepare('select ##version');
$sth1->execute;
while (#row = $sth1->fetchrow) {
$size = $row[0];
}
$size = $size * 16 / 1024;
$sth1->finish;
$sth2->execute;
while (#row = $sth2->fetchrow) {
$version = $row[0];
}
$sth2->finish;
$output = $sid.",".$size.",".$version;
$dbh->disconnect;
print $output."\n";
}
When I execute this, it crashes after 4th iteration, because the connection handle is not set. So the connection of the fifth DB does not work anymore.
Can't call method "prepare" on an undefined value at ./check_sybasedbs.pl line 36.
Line 36 is the preparation of statement 1.
I tried putting sleep commands at various positions. I also tried to explicitly clean up the variables that are reused via undef. Now I am out of ideas and would really appreciate your input.
Your code could be written as sample below (please see if ... else ... block for $dbh)
use strict;
use warnings;
use feature 'say';
use DBI;
my($user, $passwd) = qw/someuser somepassword/;
my #sids = qw/server1 server2 ... server#/;
foreach my $sid (#sids) {
my $dsn = "dbi:Sybase:server=$sid;charset=iso_1;tdsLevel=CS_TDS_50";
say "DSN: $dsn";
my $dbh = DBI->connect($dsn, $user, $passwd, { PrintError => 1,
RaiseError => 1,
AutoCommit => 1,
syb_enable_utf8 => 1
}
);
if( not defined ($dbh) ) {
say "WARNING: Could not connect to $dsn";
} else {
say "INFO: DB connection established";
my($size,$version);
my $query = 'SELECT
SUM(size)
FROM
master..sysusages
WHERE
dbid = 4
AND
segmap = 3
';
my $sth = $dbh->prepare($query);
$sth->execute;
while (#row = $sth->fetchrow) {
$size = $row[0];
}
$sth->finish;
$query = 'select ##version';
$sth = $dbh->prepare($query);
$sth->execute;
while (#row = $sth->fetchrow) {
$version = $row[0];
}
$sth->finish;
$dbh->disconnect;
$size = $size * 16 / 1024;
say "SID: $sid, SIZE: $size, VERSION: $version";
}
}
NOTE: use strict; use warnings; helps to avoid many pitfalls, use diagnostics; helps to identify a problem in difficult cases
NOTE: $sth->fetchrow_hashref allows address hash element by name, no need to count index of array as in case $sth->fetch_rowarray
in my naiv thinking I hid some lines of code that I was convinced could not be the reason for this misbehaviour. As it turns out, it was. So the reason for my problem was a simple logical error, that caused the password, that was used after a connect to a certain DB to be wrong.
Related
I want to see how much time my query taking to execute using perl. Here is my work till now
#!/usr/bin/perl -w
use DBI;
use strict;
use warnings;
use diagnostics;
use POSIX qw(strftime);
my $driver = "mysql";
my $database = "employee";
my $dsn = "DBI:$driver:database=$database";
my $userid = "root";
my $password = "****";
my $sql_statement;
my $sth;
my $data;
my $row;
my $dbh = DBI->connect($dsn, $userid, $password ) or die $DBI::errstr;
my $foo = "SET profiling=on;"."\n"."SELECT * FROM employee;"."\n"."SHOW PROFILES;";
open my $fh, "<", \$foo;
binmode $fh, ":encoding(utf8)";
while ($sql_statement = <$fh>)
{
$data = $dbh->selectall_arrayref($sql_statement) or die "issue is : $dbh->errstr";
}
my $retirement_date;
if (scalar #$data)
{
$retirement_date = strftime( "%F %H:%M:%S", localtime(time() - #$data[0]));
}
print $retirement_date;
but I am not getting any result.
expected output should be like this
5 15.86296022 SELECT * from employee;
where 5 is query id, 15.86296022 is execution time and last query.
Please suggest how to achieve this.
You're not getting any result because you're misunderstanding the data structure you're getting back from selectall_arrayref(). It's an arrayref of arrayrefs - so you need to look two levels deep in it in order to get the actual data.
I replaced the second part of your code with this:
while ($sql_statement = <$fh>) {
warn "$sql_statement\n";
$data = $dbh->selectall_arrayref($sql_statement) or die "issue is : $dbh->errstr";
print join(' | ', #$_), "\n" for #$data;
}
And when I run it, I get this:
$ perl dbprofile
SET profiling=on;
DBD::mysql::db selectall_arrayref failed: fetch() without execute() at dbprofile line 25, <$fh> line 1.
SELECT * FROM employee;
1 | Fred | 2020-09-09
2 | Bill | 2035-06-23
3 | Jane | 2058-03-11
SHOW PROFILES;
1 | 0.00027717 | SELECT * FROM employee
I'm not sure what's going on with the "fetch() without execute()" error and I don't have time to investigate it now. But you can see all of your data - including the profiling information.
Please see if following demo code provides desired output
#!/usr/bin/perl
#
# vim: ai:ts=4:sw=4
#
use strict;
use warnings;
use feature 'say';
use DBI;
use Config::General;
my($dsn,$dbh,$sth,$query,$rv);
my $conf = Config::General->new( "$ENV{HOME}/.my.cnf" );
my %config = $conf->getall;
# Keep password inside code
$config{password} = 'your_password_here';
$dsn = "DBI:mysql:database=$config{database};host=$config{host};port=$config{port}";
$dbh = DBI->connect($dsn, $config{user}, $config{password},
{
RaiseError => 1,
mysql_enable_utf8 => 1
});
$query = "SET PROFILING=on";
$sth = $dbh->prepare($query) or die $dbh->errstr;
$rv = $sth->execute() or die $dbh->errstr;
#say $DBI::errstr if $rv < 0;
my $db_table = 'your_db_table_here';
$query = "SELECT * FROM $db_table";
$sth = $dbh->prepare($query) or die $dbh->errstr;
$rv = $sth->execute() or die $dbh->errstr;
#say $DBI::errstr if $rv < 0;
$query = "SHOW PROFILES";
$sth = $dbh->prepare($query) or die $dbh->errstr;
$rv = $sth->execute() or die $dbh->errstr;
#say $DBI::errstr if $rv < 0;
while( my $row = $sth->fetchrow_arrayref ) {
say join("\t",#{$row});
}
$query = "SET PROFILING=off";
$sth = $dbh->prepare($query) or die $dbh->errstr;
$rv = $sth->execute() or die $dbh->errstr;
#say $DBI::errstr if $rv < 0;
$sth->finish();
$dbh->disconnect();
Sample of $ENV{HOME}/.my.cnf file
[mysql]
host=yout_db_host_name
user=your_db_user_name
database=your_db_name
default-character-set=utf8
port=3306
Sample of output
1 0.0014745 SELECT * FROM new_movies
I have a data file for alarms occuring during the day. The format looks like
2014/04/27-23:42:22.177742- Alarm1
2014/04/27-23:42:22.177744- Alarm2
2014/04/27-23:42:22.177747- Alarm3
2014/04/27-23:42:22.177749- Alarm1
Now I cant guess when any alarm would appear. it depends on the system. What I have done is to insert the data of alarm (e.g Alarm1) into a 2D hash. I take a chunk of 5 minutes everytime and look for alarm that appeared during the 5 minutes. I would add the value into hash, everytime I find a new alarm. In case of a repetition (like Alarm1 above) I would simply add 1 to the value. So in the end it would give me a hash which contains the alarm name and the times it appeared in 5 minutes.
Next I would start processing the next 5 minutes.
I am processing it for the whole day, so it is possible that 1 alarm can start appearing 10 in the morning, so It would be a new entry in to the hash. Now ehen I try to print the values eventually to a CSV, its a mess. Totally makes no sense. What I expect is a csv which should look like
Name,00:00,00:05,00:10,
Alarm1,2,5,2,7,
Alarm2,4,7,3,6
Alarm3,6,1,6,3
...
My code is:
use Time::Local;
use POSIX 'strftime';
use Data::Dumper;
my %outputHash= ();
$curr = timelocal(0, 0, 0, (split /\//, $ARGV[0])[1], (split /\//, $ARGV[0])[0]-1, (split /\//, $ARGV[0])[-1]);
$currentTime = strftime "%Y/%m/%d-%H:%M:%S", localtime($curr);
for ($count = 1; $count <= 288; $count++) { #there are 288 '5 minutes' in a day.
$curr += 300;
$nextTime = strftime "%Y/%m/%d-%H:%M:%S", localtime($curr);
$cmd = "awk '\$0>=from&&\$0<=to' from=\"$currentTime\" to=\"$nextTime\" Output.txt";
my $dataChunk = qx($cmd);
my #lines = split /[\n]+/, $dataChunk;
foreach my $line (#lines) {
chomp;
$timeStamp1 = substr($line,21,6);
#print "\n$timeStamp1\n$error\n";
if ($timeStamp1 != $timeStamp2){
$outputHash{$error}{$count} = $outputHash{$error}{$count} + 1;
}
$ind = index($line,'- ') + 2;
$len = length($line) - $ind;
$error = substr($line,$ind, $len);
$timeStamp2 = $timeStamp1;
}
$currentTime = $nextTime;
# if ($count>3){$count=300;}
}
`>/tmp/report.txt`;
open (MYFILE, '>>/tmp/report.txt');
my #outputArray = ();
my $flag =1;
foreach my $error (sort keys %outputHash)
{
print MYFILE "$error,";
#$outputArray[$flag][0] = $error;
for ($count=1,$count <= 288, $count++)
{
print MYFILE "$outputHash{$error}{$count},";
#$outputArray[$flag][$count] = int($outputHash{$error}{$count});
}
$flag += 1;print MYFILE "\n";
}
close (MYFILE);
#print Dumper(\#outputArray);
exit;
A simplified display of my has looks like this. The reason its haphazard is because Alarm 1 occurred only in '2nd' 5 minutes interval, Alarm 2 occured in 1st only, Alarm 3 occured in 4 consecutive 5 minutes intervals we monitored.
'Alarm1{
'2' => '5'
},
'Alarm2{
'1' => '1'
},
'Alarm3
'4' => '1',
'1' => '2',
'3' => '1',
'2' => '1'
},
Try this out, its best if you use a module that is meant for dealing with CSV.
I chose Class::CSV because it is is simple to use.
#!/usr/bin/perl
use strict;
use warnings;
use Class::CSV;
my %hash = (
'Alarm1' => {'2' => '5', },
'Alarm2' => {'1' => '1', },
'Alarm3' => {
'4' => '1',
'1' => '2',
'3' => '1',
'2' => '1'
},
);
my #fields = qw/AlarmNo 00:00:00 00:05:00 00:10:00 00:15:00/;
my $csv = Class::CSV->new( fields => \#fields );
#make the hash into a suitable array
my #array;
my #keys = keys %hash;
for my $i (0 .. $#keys){
push #{ $array[$i] }, $keys[$i];
for my $inter (1 .. 4){
my $val = '';
if(exists $hash{$keys[$i]}->{$inter}){
$val = $hash{$keys[$i]}->{$inter};
}
push #{ $array[$i] }, $val;
}
}
$csv->add_line($_) for(#array);
print join(',', #fields), "\n"; #Just to make it tidy on the commandline
$csv->print();
So you would use print MYFILE $csv->string to get it into your file.
Edit:
If you can't install Class::CSV check out Text::CSV which may be installed by default.
You can also join the arrays by a comma like so
for(#array){
print join(',', #{$_});
}
Very new to Perl. I am having issues trying to get DBI to communicate to a SQL server 2008 DB.
I get the following error when I try and connect to SQL Servereveb when I try to use ODBC or directly.
I am new to Perl, can someone please assist...thanks
install_driver(MSSQL) failed: Can't locate object method "set_sql" via package "Class::DBI::MSSQL" at C:/Perl/lib/DBD/MSSQL.pm line 79. Compilation failed in require at (eval 19)C:/Perl/site/lib/DBI.pm:744 line 3.
use strict;
use warnings;
use diagnostics;
use Class::DBI::Loader;
use DBI;
use File::Glob ':glob';
my $DBUserName = "*******";
my $DBPassword = "*******";
my $DBName = "dbi:MSSQL:uat-dbserver1";
my $dbh = "";
my $sqlStatement = "";
my $sqlCmd = "";
my #EasySetTableNames = ();
$dbh = DBI->connect( $DBName, $DBUserName, $DBPassword,
{ PrintError => 0, AutoCommit => 0})
|| die "Database connection creation failed: $DBI::errstr\n";
$sqlStatement = "SELECT * from tableA ";
$sqlCmd = $dbh->prepare($sqlStatement);
$sqlCmd->execute();
#EasySetTableNames = #{$dbh->selectcol_arrayref($sqlStatement)};
print "hi";
and via ODBC
#!/usr/bin/perl -w
use strict;
use DBI;
# Replace datasource_name with the name of your data source.
# Replace database_username and database_password
# with the SQL Server database username and password.
my $data_source = "dbi:MSSQL:test";
my $user = "test";
my $password = "test";
# Connect to the data source and get a handle for that connection.
my $dbh = DBI->connect($data_source, $user, $password)
or die "Can't connect to $data_source: $DBI::errstr";
# This query generates a result set with one record in it.
my $sql = "SELECT 1 AS test_col";
# Prepare the statement.
my $sth = $dbh->prepare($sql)
or die "Can't prepare statement: $DBI::errstr";
# Execute the statement.
$sth->execute();
# Print the column name.
print "$sth->{NAME}->[0]\n";
# Fetch and display the result set value.
while ( my #row = $sth->fetchrow_array ) {
print "#row\n";
}
# Disconnect the database from the database handle.
$dbh->disconnect;
Any help you can provide would be so appreciated.
I usually use the ODBC driver from within the dbi and this is how I would usually hit sql server (2008 r2)
#!/export/appl/pkgs/perl/5.8.4-fiq/bin/perl
#!/usr/bin/perl
#!/bin/sh
use strict;
use DBI;
use Time::localtime;
use Data::Dumper;
my $dsn = 'DBI:ODBC:Driver={SQL Server}';
my $host = 'xxx\yyy';
my $database = 'testing';
my $user = 'user';
my $auth = 'password';
my $dbh = DBI->connect("$dsn;Server=$host;Database=$database", $user, $auth) or die "Database connection not made: $DBI::errstr";
my $sql = "EXECUTE database.schema.sproc";
my $stmt = $dbh->prepare($sql);
$stmt->execute();
$stmt->finish();
$dbh->disconnect;
I was able to connect to SQL Server using OLE32, here is an example of the code..."Cursor type changed" error on Perl OLE32 MSSQL dateadd function results
I understand there are MANY ways to do all of this, but trying to do it the best way.
I have created the db parameters, dns, dbh, sth, sql and generally quite happy with the result up to ... well ... the result part.
<?php
// db parameters
$dbhost = "localhost";
$dbname = "x";
$dbuser = "y";
$dbpass = "z";
// driver invocation (dsn is short for data source name)
$dsn = "mysql:host=$dbhost;dbname=$dbname";
// create db object (dbh is short for database handle)
$dbh = new PDO($dsn, $dbuser, $dbpass);
// execution of database query (sth is short for statement handle)
$sql = "SELECT * FROM a_aif_remaining";
$sth = $dbh->prepare($sql);
$sth->execute();
NOT SURE WHAT TO PUT BELOW.... (A) or (B)
I just want to present a simple array of the data. One row from the table per line.
Option A
echo $_POST['fieldname1'];
echo $_POST['fieldname2'];
echo $_POST['fieldname3'];
Option B
while ($rows = $sth->fetch(PDO::FETCH_ASSOC)) {
echo $row[fieldname1],'<br>';
}
AND I AM CONFIDENT WITH THE ENDING
$dbh = NULL;
?>
Any advise would be GREATLY appreciated.
UPDATED CODE: (Produces nothing on the page)
<?php
// db parameters
$dbhost = "localhost";
$dbname = "theaudit_db1";
$dbuser = "theaudit_user";
$dbpass = "audit1999";
$dsn = "mysql:host=$dbhost;dbname=$dbname"; // driver invocation (dsn is short for Data Source Name)
try {
$dbh = new PDO($dsn, $dbuser, $dbpass); // connect to new db object (dbh is short for Database Handle)
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // set the PDO error mode to enable exceptions
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); // set the PDO emulate prepares to false
// execute query to database (sth is short for Statement Handle)
$sql = "SELECT * FROM a_aif_remaining";
$sth = $dbh->prepare($sql);
$sth->execute();
$data = $sth->fetchAll(PDO::FETCH_ASSOC);
$dbh = NULL;
}
catch(PDOException $e)
{
echo $e->getMessage();
}
?>
Though I can't get what's the connection between A anb B, I can answer the
I just want to present a simple array of the data. One row from the table per line.
question.
$sql = "SELECT * FROM a_aif_remaining";
$sth = $dbh->prepare($sql);
$sth->execute();
$data = $sth->fetchAll(PDO::FETCH_ASSOC);
where $data is a sought-for array.
The problem with your updated code is simple - you arent echo'ing your data out. You need to add something like..
foreach($data as $arKey=>$dataRow){
foreach($dataRow as $arKey=>$rowField){
echo $rowField.','; //concat with a ',' to give csv like output
}
echo '<br>'; //to get to next line for each row (may want to trim the last ','
}
I am also confused by the reference to $_POST. It is true both are associate arrays but that does not mean that the $_POST option is viable - the data would only be available in the $_POST if you put it there (eg $_POST = $data) which would be pointless. Or if you had posted the data from somewhere else. Neither seem to fit what you are asking so I would forget about the $_POST and just figure out how you access your multi dimensional $data array. There is endless tut's on this subject. Try using
var_dump($data)
to see whats inside that should help you visualise what is going on.
NOTE: in option B you are not correctly concatenating or referencing your array it should be:
while ($rows = $sth->fetch(PDO::FETCH_ASSOC)) {
echo $rows[fieldname1].'<br>'; //$row doesnt exist its $rows and you use . to concat not ,
}
Ah yes and probably better to use unset rather than setting $dbh to equal null
unset($dbh);
I have a Perl script which takes a file as input and has PL/SQL (for statements and DBMS_OUTPUT.PUT_LINE) in it. I need to run make a database connection and run that file in Perl script.The pl/sql has Begin declare end section,with for statements on it which is writing data of 3 columns separated using commas(DBMS_OUTPUT.PUT_LINE) I have shown what I have tried below. Is it correct?
my $dbh = DBI->connect( "dbi:Oracle:$db", $username, $passwd ) ||
die( $DBI::errstr . "\n" );
$dbh->{AutoCommit} = 0;
$dbh->{PrintError} = 1;
open FILE, $sql_file or die "Could not open file";
$query = '';
while($line = <FILE>) {
chomp($line);
$query .= $line;
}
my $sth = $dbh->prepare($query);
$sth->execute();
while ( my #row = $sth->fetchrow_array() ) {
foreach (#row) {
print "$_";
}
print "\n";
}
$sth->fetch(), $sth->fetchrow_*(), and friends all fetch records from a result set. In Oracle PL/SQL blocks don't normally return result sets. So calling $sth->fetchrow_array() after running a PL/SQL block won't return any results.
If your using DBMS_OUTPUT.PUT_LINE to output results you will need to use the dbms_output_* functions provided by DBD::Oracle.
my $dbms_output_byte_limit = 1000000;
$dbh->func( $dbms_output_byte_limit, 'dbms_output_enable' );
my $sth = $dbh->prepare($query);
$sth->execute();
my #text = $dbh->func( 'dbms_output_get' );