I am able to fetch a data from db not able to display in browser.
below is the code-
my $q = CGI->new;
print $q->header,$q->start_html('testing');
my $title = $q->param('title');
my $perl = "";
#these is displayed properly
print "<font color=blue><b>TITLE:\"$title\"</b><br>";
print "<font color=blue><b>SCRIPT:\"$title\"</b>\n";
my $dbh = DBI->connect("DBI:ODBC:test","username","password") || die "Connection error: $DBI::errstr\n";
my $sql = "select * from tablename where title = '$title'";
my $sth = $dbh->prepare($sql);
$sth->execute;
my #row = $sth->fetchrow_array;
for(my $i=1;$i<=#row;$i++)
{
if($i == 5)
{
$perl = "$row[$i]";
}
}
#below is not displayed in browser
print $q->strong($title);
print $q->strong($perl);
$sth->finish();
$dbh->disconnect;
print $q->end_html;
I just want to print the value of $title and $perl in browser.
this program is running properly but cant able to display value of $title and $perl
The reason for the failure is not obvious to me, but you should use placeholders when performing queries:
my $sql = "select * from tablename where title = ?"; # placeholder
my $sth = $dbh->prepare($sql);
$sth->execute($sql); # $sql is used here
The placeholder is a question mark ?. This will ensure that your values are quoted properly, and prevent injection attacks. Using the data from the CGI object without sanitizing it is very dangerous.
Also, it seems that you are only taking one value from the array, so there is little need to use a loop in the first place. You could just do:
my $row = $row[5];
To see if the value was in the database, you can use if (defined $row), or if (#row >= 6). (Note that arrays start at 0, so the element with index 5 is actually the 6th element. Just pointing this out since you started your loop at 1.)
Try running it straight from the command line, without the browser.
See here and here.
You can also use the Perl debugger, if you start it with:
perl -d yourprogram
Related
I am very new to perl and this is my first time using any perl script. I have a script to parse emailIDs from pubmed
#!/usr/bin/perl -w
# A perlscript written by Joseph Hughes, University of Glasgow
# use this perl script to parse the email addressed from the affiliations in PubMed
use strict;
#use LWP::Simple;
use LWP::Protocol::https;
use LWP::UserAgent;
my ($query,#queries);
#Query the Journal of Virology from 2014 until the present (use 3000)
$query = 'journal+of+virology[journal]+AND+2014[Date+-+Publication]:3000[Date+-+Publication]';
push(#queries,$query);
#Journal of General Virology
$query = 'journal+of+general+virology[journal]+AND+2014[Date+-+Publication]:3000[Date+-+Publication]';
push(#queries,$query);
#Virology
$query = 'virology[journal]+AND+2014[Date+-+Publication]:3000[Date+-+Publication]';
push(#queries,$query);
#Archives of Virology
$query = 'archives+of+virology[journal]+AND+2014[Date+-+Publication]:3000[Date+-+Publication]';
push(#queries,$query);
#Virus Research
$query = 'virus+research[journal]+AND+2014[Date+-+Publication]:3000[Date+-+Publication]';
push(#queries,$query);
#Antiviral Research
$query = 'antiviral+research[journal]+AND+2014[Date+-+Publication]:3000[Date+-+Publication]';
push(#queries,$query);
#Viruses
$query = 'viruses[journal]+AND+2014[Date+-+Publication]:3000[Date+-+Publication]';
push(#queries,$query);
#Journal of Medical Virology
$query = 'journal+of+medical+virology[journal]+AND+2014[Date+-+Publication]:3000[Date+-+Publication]';
# global variables
push(#queries,$query);
my %emails;
my $emailcnt=0;
my $count=1;
#assemble the esearch URL
foreach my $query (#queries){
my $base = 'http://eutils.ncbi.nlm.nih.gov/entrez/eutils/';
my $url = $base . "esearch.fcgi?db=pubmed&term=$query&usehistory=y";
#my $url = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&term=journal+of+medical+virology[journal]+AND+2014[Date+-+Publication]:3000[Date+-+Publication]&usehistory=y";
print "\n before url \n";
print $url;
#post the esearch URL
#my $output = get($url);
my $ua = LWP::UserAgent->new(timeout => 30);
$ua->ssl_opts( verify_hostname => 0 );
my $response = $ua->get($url);
print "before response";
print $response;
unless ($response->is_success) {
# the Client-Warning, Client-Aborted, and X-Died headers each may be set on client/transport errors
die $response->status_line;
}
my $output = $response->decoded_content;
print "\n before output \n";
print $output;
#parse WebEnv, QueryKey and Count (# records retrieved)
my $web = $1 if ($output =~ /<WebEnv>(\S+)<\/WebEnv>/);
my $key = $1 if ($output =~ /<QueryKey>(\d+)<\/QueryKey>/);
my $count = $1 if ($output =~ /<Count>(\d+)<\/Count>/);
#retrieve data in batches of 500
my $retmax = 500;
for (my $retstart = 0; $retstart < $count; $retstart += $retmax) {
my $efetch_url = $base ."efetch.fcgi?db=pubmed&WebEnv=$web";
$efetch_url .= "&query_key=$key&retmode=xml";
my $efetch_out = LWP::UserAgent->new(timeout => 30)->get($efetch_url);
my #matches = $efetch_out =~ m(<Affiliation>(.*)</Affiliation>)g;
#print "$_\n" for #matches;
for my $match (#matches){
if ($match=~/\s([a-zA-Z0-9\.\_\-]+\#[a-zA-Z0-9\.\_\-]+)$/){
my $email=$1;
$email=~s/\.$//;
$emails{$email}++;
}
}
}
my $cnt= keys %emails;
print "$query\n$cnt\n";
}
print "Total number of emails: ";
my $cnt= keys %emails;
print "$cnt\n";
my #email = keys %emails;
my #VAR;
push #VAR, [ splice #email, 0, 100 ] while #email;
my $batch=100;
foreach my $VAR (#VAR){
open(OUT, ">Set_$batch\.txt") || die "Can't open file!\n";
print OUT join(",",#$VAR);
close OUT;
$batch=$batch+100;
}
This is running fine but after running it, I am getting Total number of emails: 0 which I am pretty sure is not actually the case.
Do we know what is going on here? I can see results like this
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE eSearchResult PUBLIC "-//NLM//DTD esearch 20060628//EN" "https://eutils.ncbi.nlm.nih.gov/eutils/dtd/20060628/esearch.dtd">
<eSearchResult><Count>1552</Count><RetMax>20</RetMax><RetStart>0</RetStart><QueryKey>1</QueryKey><WebEnv>NCID_1_108762718_130.14.18.34_9001_1545718459_2017377343_0MetA0_S_MegaStore</WebEnv><IdList>
<Id>30578684</Id>
<Id>30578670</Id>
<Id>30575982</Id>
<Id>30570784</Id>
<Id>30570771</Id>
<Id>30570770</Id>
<Id>30570759</Id>
<Id>30570750</Id>
<Id>30560545</Id>
<Id>30552705</Id>
<Id>30549048</Id>
<Id>30548936</Id>
<Id>30548642</Id>
<Id>30537228</Id>
<Id>30537157</Id>
<Id>30516836</Id>
<Id>30515847</Id>
<Id>30512182</Id>
<Id>30512180</Id>
<Id>30489644</Id>
after the get request has run
Source of script: Script link on github
This script could do with some refactoring, so one can actually debug and maintain it.
Probably using an object oriented approach would help greatly,
for me, regexing the #queries declaration into a proper list declaration, and clearing out some cruft, renaming variables to what they actually do instead of commenting ( retmax -> batch_size ? ) seem like obvious low hanging fruit.
Handling errors would be next on my list ( what happens if there are no query parameters found? The script continues despite a count of 0 / none found, such behavior is confusing when debugging )
I have not done this for you, and I am also not sure whether a web scraper for emails is actually a great thing to have, but that aside:
If you change the line you commented out #print "$_\n" for #matches; to print "no emails found for query '$query'\n" unless #matches; you will see clearly why the script does not do what you want.
Without running the code myself, I would speculate that the #matches array in the code is empty. Try un-commenting the print statement to verify whether or not #match is being assigned as expected, and work backward or forward from there to narrow down which statement(s) are not doing what they are expected to.
my #matches = $efetch_out =~ m(<Affiliation>(.*)</Affiliation>)g;
#print "$_\n" for #matches;
That being said, ideally this code should be completely rewritten in a less procedural style for maintainability.
I wrote this piece of code: it is supposed to keep checking names until it finds one that does not exist, and then it should go on.
while ($repeating == 1) {
$new_name = $i . "_" . $file;
my $sql= "SELECT file_name FROM PDFdocument WHERE user_id = '$id' AND file_name = '$new_name' ";
my $sth = $dbh->prepare($sql);
$sth->execute();
while (my #row = $sth->fetchrow_array) {
//never enters here
if ($new_name ne $row[0]) {
$repeating = 0;
}
}
$i++;
}
It never enters the second while loop, so it gets stuck in this repeating loop. I don't know why it does not work; I do some other sql statements before and they all work. This is the only one that does not work.
Any help?
The problem is that you won't get any rows back if the name does not exist. The solution is to just check if you do get any rows - otherwise the file name mus be unused. BTW, let DBI escape stuff you sent to the database. This should work:
while ($repeating == 1) {
$new_name = $i . "_" . $file;
# the question marks are placeholders
my $sql= "SELECT file_name FROM PDFdocument WHERE user_id = ? AND file_name = ? ";
my $sth = $dbh->prepare($sql);
# filling the placeholders while executing
$sth->execute($id, $new_name);
if(!$sth->fetch) {
# no rows found? this name must be fresh
$repeating = 0;
}
$i++;
}
Edit: As #ikegami mentioned in a comment, the behavior of $sth->rows depends on the driver, so it might return different values for different database engines when dealing with SELECT statements (see also the DBI docs. Asking the driver to fetch a row should work the same on all drivers.
Keep in mind that this is susceptible to race conditions, i.e. if two scripts run at the same time, they might both chose the same "unused" filename. Make sure you're using some kind of locking mechanism to avoid that.
When you finally find the $i you should use, $sth->fetchrow_array returns an empty list, so = returns 0, so the loop isn't entered.
Solution 1:
my $new_name;
for (my $i=1; ; ++$i) {
$new_name = $i . "_" . $file;
$dbh->selectrow_arrayref(
"SELECT 1 FROM `PDFdocument` WHERE `user_id` = ? AND `file_name` = ?",
undef,
$id, $new_name,
)
and last;
}
Solution 2:
my $i = $dbh->selectrow_array(
"
SELECT CAST(LEFT(`file_name`, LOCATE("_", `file_name`)-1) AS INT) AS `i`
FROM `PDFdocument`
WHERE `user_id` = ?
AND `file_name` LIKE ?
ORDER BY DESC `i`
LIMIT 1
",
undef,
$id, "%\\_\Q$file\E"
);
++$i;
my $new_name = $i . "_" . $file;
Note the use of placeholders. Your buggy way of building the SQL statement leaves you vulnerable to malfunctions if not attacks.
I am required to pull out rows corresponding to column name. The rows being pulled out correspond to address in array #values. Following is my code:
use strict;
use DBI;
open (FH, "/user/address") or die $!;
my#values=<FH>;
close(FH);
my #names;
my $query = "Select name from table where address = ?";
my $sth = $dbh->prepare( $query ) or die "could not prepare statement\n", $dbh->errstr;
foreach my $value(#values){ ##values contain list of address
$sth->execute($value) or die "could not execute statement $query\n", $sth->errstr;
while ($result = $sth->fetchrow_hashref()){
my $name_reqd = $result->{name};
print "Name Req: $name_reqd\n"; #not printing anything
push (#names, $name_reqd);
}
}
print "#names\n"; #not printing anything
But when I print #names, I don't get any output, I am unsure as to what is going wrong.
I see a few things:
you aren't declaring #names before you use it, which means it is being automatically declared at the scope of its first use: inside the while loop. Moreover a new copy is destroyed and created on every iteration of the loop, and then a new (empty) one is created when you call print. Adding use warnings; would catch this.
(as mobrule and others have said) you may not be accessing the data correctly out of $result. Try adding use Data::Dumper; print Dumper($result); in the top line of the while loop to see what data you have read in.
I'm not sure what format you are using in /usr/address, but if it has more than one line, you're only reading the first line from that file. You can read in the entire file in one go by localizing $/ first (see perldoc perlvar). Moreover (as Ivan said), this string will still have a newline at the end: use chomp to strip it (perhaps after splitting into lines, if you slurped more than one). See perldoc -f chomp and perldoc -f split.
Between these points, you should have enough debugging data being printed that you should easily see where you went wrong.
The problem with this code is file reading. Code my #values=<FH>; reads all lines with new-line (\n) symbol at the end. It should be manually removed in this case. You can do it using chomp function:
open (FH, "/user/address") or die $!;
my #values = <FH>;
chomp(#values);
close(FH);
Update:
I think it's not searching anything because it just can't find. Addresses usually have spaces within. Query Select name from table where address = ? will find only exact equal addresses (letter case is the only thing ignored). For example " a" is not equal to "a" in sql.
untested, but shouldn't you access your fields like this.
my $name_reqd = $result[0];
This is because you are using fetchrow_array().
You should ensure that the database driver is returning column names in lower case. Also, use selectcol_arrayref for this type of query:
use strict;
use warnings;
use DBI;
use File::Slurp;
my #values = read_file '/user/address';
chomp #values;
my $dbh = DBI->connect(
# appropriate parameters
);
my $sth = $dbh->prepare(
'SELECT name FROM table WHERE address = ?'
) or die sprintf 'Cannot prepare: %s', $dbh->errstr;
my #names;
for my $value ( #values ) {
my $names = $dbh->selectcol_arrayref(
$sth, {}, $value
) or die sprintf 'Cannot select col: %s', $dbh->errstr;
push #names, #$names;
print "'$_'\n" for #$names;
);
print "#names\n";
DBI performs case conversion on column names, so it may be returing the result in the key "NAME" instead of "name".
What do you see if you print keys %$result after calling $sth->fetchrow_hashref ?
Is your table really called "table"? Maybe it should be $table, with $table set to the actual name of the table.
If you can, for example SQLite or MySQL have this, try running your query from the database command line.
Our website uses Perl to provide a simple mechanism for our HR people to post vacancies to our website. It was developed by a third party, but they have been long since kicked into touch, and sadly we do not have any Perl skills in-house. This is what happens when Marketing people circumvent their in-house IT team!
I need to make a simple change to this application. Currently, the vacancies page says 'We currently have the following vacancies:', regardless of whether there are any vacancies! So we want to change it so that this line is only displayed at the appropriate times.
I could, obviously, start to learn a bit of Perl, but we are already planning a replacement site, and it certainly won't be using Perl. So since the solution will be trivial for those with these skills, I thought I'd ask for some focused help.
Below is the start of the procedure that lists the vacancies.
sub list {
require HTTP::Date;
import HTTP::Date;
my $date = [split /\s+/, HTTP::Date::time2iso(time())]->[0];
my $dbh = DBI->connect($dsn, $user, $password)
|| die "cannot connect to $database: $!\n";
my $sql = <<EOSQL;
SELECT * FROM $table where expiry >= '$date' order by expiry
EOSQL
my $sth = $dbh->prepare($sql);
$sth->execute();
while (my $ref = $sth->fetchrow_hashref()) {
my $temp = $template;
$temp =~ s#__TITLE__#$ref->{'title'}#;
my $job_spec = $ref->{'job_spec'};
...etc...
The key line is while (my $ref = $sth->fetchrow_hashref()) {. I'm figuring that this is saying 'while I can pull off another vacancy from the returned recordset...'. If I place my print statement before this line, it will always be shown; after this line and it was be repeated for every vacancy.
How do I determine that there are some vacancies to be displayed, without prematurely moving through the returned recordset?
I could always copy the code within the while loop, and place it within an if() statement (preceding the while loop) which will also include my print statement. But I'd prefer to just have the simpler approach of If any records then print "We currently have.." line. Unfortunately, I haven't a clue to code even this simple line.
See, I told you it was a trivial problem, even considering my fumbled explanation!
TIA
Chris
A really simple way would be:
$sth->execute();
my $first = 1;
while (my $ref = $sth->fetchrow_hashref()) {
if( $first ) {
print "We currently have the following vacancies:\n";
$first = 0;
}
my $temp = $template;
...
}
if( $first ) {
print "No vacancies found\n";
}
If you are using Mysql, the "rows" method works just fine:
$sth->execute();
if($sth->rows) {
print "We have data!\n";
}
while(my $ref = $sth->fetchrow_hashref()) {
...
}
The method, and some caveats, are documented in extensive detail in "perldoc DBI". Always start with "perldoc".
This isn't so much a Perl question as it's a database question, and there is no good way to know how many results you have until you have them. You've got two choices here:
Do a query that does a "select count(*)" to see how many rows there are, and then another query to get the actual rows or
Do the query and store the results into a hash, then count how many entries you have in the hash, and then go through the hash and print out the results.
For example, off the top of my head:
my #results = ();
while (my $ref = $sth->fetchrow_hashref()) {
push #results, $ref;
}
if ($#results == 0) {
... no results
} else {
foreach $ref (#results) {
my $temp = $template;
....
}
Since everyone wants to optimize away the repeated tests for whether the header has been printed in Graeme's solution, I present this minor variation on it:
$sth->execute();
my $ref = $sth->fetchrow_hashref();
if ($ref) {
print "We currently have the following vacancies:\n";
while ($ref) {
my $temp = $template;
...
$ref = $sth->fetchrow_hashref();
}
} else {
print "No vacancies found\n";
}
Since your query is a SELECT, you cannot take advantage of rows or of the value returned by the execute itself.
However, you can pre-count how many rows (i.e. vacancies) your query will select by adding another query... something like this:
# Retrieve how many vacancies are currently offered:
my $query = "SELECT COUNT(*) AS rows FROM $table WHERE expiry >= ?";
$sth = $dbh->prepare($query);
$sth->execute($date);
$numVacancies = $numinfo->fetchrow_arrayref()->[0];
# Debug:
print "Number of vacancies: " . $numVacancies . "\n";
if ( $numVacancies == 0 ) { # no vacancy found...
print "No vacancies found!\n";
}
else { # at least a vacancy has been found...
print "We currently have the following vacancies:\n";
# Retrieve the vacancies:
my $sql = "SELECT * FROM $table where expiry >= '$date' ORDER BY expiry";
my $sth = $dbh->prepare($sql);
$sth->execute();
...
}
Or, similarly, instead of "prepare" and "execute" the query and then use "fetchrow_array", you can do everything in a single call using selectrow_array:
# Retrieve how many vacancies are currently offered:
my $query = "SELECT COUNT(*) AS rows FROM $table WHERE expiry >= ?";
my $numVacancies = $dbh->selectrow_array($query, undef, $date);
# Debug:
print "Number of vacancies: " . $numVacancies . "\n";
And the same is also true for selectall_arrayref:
# Retrieve how many vacancies are currently offered:
my $query = "SELECT COUNT(*) AS rows FROM $table WHERE expiry >= ?";
my $numVacancies = $dbh->selectall_arrayref($query, {Slice => {}}, $date);
# Debug:
print "Number of vacancies: " . #$numVacancies[0]->{rows} . "\n";
However, if you use selectrow_array or selectall_arrayref, you can also retrieve the number of vacancies directly from the result of the original query:
# Retrieve the vacancies:
my $sql = "SELECT * FROM $table where expiry >= ? ORDER BY expiry";
my $vacancies = $dbh->selectall_arrayref($sql, {Slice => {}}, $date);
# Debug:
print "Number of vacancies: " . scalar #{$vacancies} . "\n";
A bit more efficient way (avoiding a conditional inside the loop), if you don't mind it changing the way the page is output a bit (all at once rather than a row at a time) you could make a variable to hold the output just before the loop:
my $output = '';
and then inside the loop, change any print statement to look like this:
$output .= "whatever we would have printed";
then after the loop:
if ($output eq '')
{
print 'We have no vacancies.';
}
else
{
print "We currently have the following vacancies:\n" . $output;
}
Just add another query.. something like this:
# count the vacancies
$numinfo = $dbh->prepare("SELECT COUNT(*) FROM $table WHERE EXPIRY >= ?");
$numinfo->execute($date);
$count = $numinfo->fetchrow_arrayref()->[0];
# print a message
my $msg = '';
if ($count == 0) $msg = 'We do not have any vacancies right now';
else $msg = 'We have the following vacancies';
print($msg);
use Lingua::EN::Inflect 'PL';
$sth->execute();
my $results = $sth->fetchall_arrayref( {}, $max_rows );
if (#$results) {
print "We currently have the following ", PL("vacancy",scalar #$results), ":\n";
for my $ref (#$results) {
...
}
}
Says perldoc DBI:
For a non-"SELECT" statement, "execute" returns the number of rows
affected, if known. If no rows were affected, then "execute"
returns "0E0", which Perl will treat as 0 but will regard as true.
So the answer is to check the return value of $sth->execute():
my $returnval = $sth->execute;
if (defined $returnval && $returnval == 0) {
carp "Query executed successfully but returned nothing";
return;
}
I am having trouble with a very simple Perl process. I am basically querying an Oracle database and I want to load it into Excel. I have been able to use DBIx::Dump and it works. However, I need to be able to use a variety of Excel formatting tools. And I think Spreadsheet::WriteExcel is the best module that outputs to Excel that allows me do more formatting.
Below is the code and the error I am getting. I basically query Oracle, fetch the data, load into an array and try to write to Excel. For some reason it is doing some kind of comparison and it does not like the data types. For example, the date is '25-OCT-08'. The SVP is 'S01'. It seems to be saying that they are not numeric.
Error:
Argument "01-NOV-08" isn't numeric in numeric ge <>=> at C:/Perl/site/lib/Spreadsheet/WriteExcel/Worksheet.pm line 3414.
Argument "01-NOV-08" isn't numeric in pack ge <>=> ge <>=> at C:/Perl/site/lib/Spreadsheet/WriteExcel/Worksheet.pm line 2157.
Code:
#!/usr/bin/perl -w
#Set the Perl Modules
use strict;
use DBI;
use Spreadsheet::WriteExcel;
# Connect to the oracle database
my $dbh = DBI->connect( 'dbi:Oracle:xxxx',
'xxxx',
'xxxx',
) || die "Database connection not made: $DBI::errstr";
#Set up Query
my $stmt = "select
week_end_date, SVP, RD,
DM, store, wtd_smrr_gain,QTD_SMRR_GAIN,
wtd_bor_gain,QTD_BOR_GAIN,
wtd_cust_gain,QTD_CUST_GAIN,
wtd_CARD_CLOSED_OCT25,QTD_AVG_CARD_CL
from
bonus_4Q_store
order by
store";
#Prepare Query
my $sth = $dbh->prepare($stmt);
#Execute Query
$sth->execute() or die $dbh->errstr;
my( $week_end_date,$SVP,$RD,$DM,$store,
$wtd_smrr_gain,$QTD_SMRR_GAIN,
$wtd_bor_gain,$QTD_BOR_GAIN,
$wtd_cust_gain,$QTD_CUST_GAIN,
$wtd_CARD_CLOSED_OCT25,$QTD_AVG_CARD_CL);
#binds each column to a scalar reference
$sth->bind_columns(undef,\$week_end_date,\$SVP,\$RD,\$DM,\$store,
\$wtd_smrr_gain,\$QTD_SMRR_GAIN,
\$wtd_bor_gain,\$QTD_BOR_GAIN,
\$wtd_cust_gain,\$QTD_CUST_GAIN,
\$wtd_CARD_CLOSED_OCT25,\$QTD_AVG_CARD_CL,);
#create a new instance
my $Excelfile = "/Test_Report.xls";
my $excel = Spreadsheet::WriteExcel->new("$Excelfile");
my $worksheet = $excel->addworksheet("WOW_SHEET");
#Create array shell
my #data;
#Call data and Write to Excel
while ( #data = $sth->fetchrow_array()){
my $week_end_date = $data[0];
my $SVP = $data[1];
my $RD = $data[2];
my $DM = $data[3];
my $store = $data[1];
my $wtd_smrr_gain = $data[2];
my $QTD_SMRR_GAIN = $data[3];
my $wtd_bor_gain = $data[4];
my $QTD_BOR_GAIN = $data[5];
my $wtd_cust_gain = $data[6];
my $QTD_CUST_GAIN = $data[7];
my $wtd_CARD_CLOSED_OCT25 = $data[8];
my $QTD_AVG_CARD_CL = $data[9];
my $row = 0;
my $col = 0;
foreach my $stmt (#data)
{
$worksheet->write($row++, #data);
last;
}
}
print "DONE \n";
$sth->finish();
$dbh->disconnect();
The problem is here:
foreach my $stmt (#data)
{
$worksheet->write($row++, #data); # !!
last;
}
The correct syntax for write() is:
write($row, $column, $token, $format)
You are missing the $column argument, which in this case is probably 0.
If $stmt is an array ref then you can write it in one go as follows:
$worksheet->write($row++, 0, $stmt);
I would guess that it is coming out as a string, and when you try to insert it into the date column, there is no implicit conversion for it.
Try selecting the date like this, and it will turn it into a char that you can use to do compares.
to_char(date, 'YYYY/MM/DD HH24:MI:SS')
then
to_date(date, 'YYYY/MM/DD HH24:MI:SS')
to convert it back to a date on insert. That is generally what you need to do in SQL.
As I recall, perl has a trace facility for DBI that might giver a better picture as to what is going on.