How can I show the query time in Perl, DBI? - mysql

I use Perl and DBI to manage my MySQL tables, querys, etc. How can I show the running time of a query?
If I do a SELECT in the console, the result will be like this:
+-----+-------------+
| id | name |
+-----+--------------
| 1 | Jack |
| 2 | Joe |
| 3 | Mary |
+-----+-------------+
3 rows in set (0.17 sec)
I need to show 0.17 sec. There is any way in DBI to show the running time in Perl, something like this?
my $dbh = $db->prepare("SELECT id, name FROM names ORDER BY id;");
$dbh->execute;
print $dbh->runnin_time; # ???

DBI#Profile, DBI::Profile, DBI::ProfileData, DBI::ProfileDumper, dbiprof

You take a timestamp before you run the query, and a timestamp after. The difference is your query execution time. For obtaining high-resolution timestamps, see Time::HiRes

I can't find anything in DBI. I think that there is nothing already implemented out of the box, though could be interesting information.
The other way to do this would be to get the time before and after the execution and then make a simple difference. You can do it from within your Perl script simply getting the time stamp before the query execution, and after, then subtract the two to find the execution time.
my $start = DateTime->now;
my $dbh = $db->prepare("SELECT id, name FROM names ORDER BY id;");
$dbh->execute;
my $end = DateTime->now;
my $elapsedtime = ($end->subtract_datetime($start))->seconds;
print "Execution time(seconds) : $elapsedtime \n";

Using Time::HiRes could also be an easy way to find the query time. Here is an example that uses Time::HiRes:
use Time::HiRes;
$start_time = Time::HiRes::gettimeofday();
my $dbh = $db->prepare("SELECT id, name FROM names ORDER BY id;");
$dbh->execute;
$end_time = Time::HiRes::gettimeofday();
my $elapsedtime = sprintf("%.6f", $end_time - $start_time);
print "Execution time(seconds) : $elapsedtime \n";

Reading #daxim's links to documentation, there is a simple way to achieve this by running your script with DBI_PROFILE=2 which is from DBI::Profile
Example output:
DBI::Profile: 53.203692s 50.67% (6725 calls) script.pl # 2016-01-21 11:51:49
'INSERT INTO FOO ("BAR") VALUES (?)' =>
0.057596s / 2 = 0.028798s avg (first 0.051621s, min 0.005975s, max 0.051621s)
'INSERT INTO BAZ ("QUX") VALUES (?)' =>
0.367184s / 44 = 0.008345s avg (first 0.039410s, min 0.002445s, max 0.039410s)

Related

Regular expressions on strings in SQL statement

I have a table with following columns and sample data
File Name | Status
'xxx_2015-07-20.csv' | Completed
'xxx_2015-07-19.csv' | Completed
'xxx_2015-07-18.csv' | Failed
.
.
'xxx_2015-06-01.csv' | Failed
Now I have two scenarios in my application (PHP-MySQL):
1) I have to fetch the status of today's file. I can do it like this:
select status from myTable where file_name like '%date(Y-m-d)%';
2) I want to fetch the status of all files generated since 1 month from today. Suppose today is 2015-07-20, then files from 2015-06-20 should show up.
Since there is no date column, I can't apply comparison operator and get the results. I believe it will need some playing with regular expressions.
I am not familiar with regular expressions, so any help on this would be much appreciated.
If the pattern is same i.e. xxx_2015-07-20.csv you can use substring_index function to get the date value as
mysql> select substring_index(substring_index('xxx_2015-07-20.csv','_',-1),'.',1) as d;
+------------+
| d |
+------------+
| 2015-07-20 |
+------------+
Now using the same you can have the select statement as
select status from myTable
where
substring_index(
substring_index(file_name,'_',-1),
'.',1
) = curdate();

how to return only query time and not the data

I use the SQuirreL application from sourceforge to query and extract info from my data sources. Some of the data though are huge and I don't really need to see it, I am only interested in how long it took to run the whole query.
So I am wondering if there is any command in SQL that I can add to a SELECT statement to run the query but not bring back any data just the time that it took to retrieve the data?
Thanks
You can use MySQL Query profiler to do something similar:
mysql> set profiling=1;
select * from table;
mysql> show profiles;
This should show you something like:
+----------+------------+-----------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+-----------------------------------------------+
| 0 | 0.00007300 | set profiling=1 |
| 1 | 0.00044700 | select * from table; |
+----------+------------+-----------------------------------------------+
2 rows in set (0.00 sec)
Alternately you could do something hacky with microtime(), something like:
// Grab the start time
$start = microtime(true);
// Execute your query
mysql_query('select * from table');
// Do something with the execution time
echo microtime(true) - $start .' seconds';
But obviously this is far from ideal (and you shouldn't be using mysql_*)!

mysql order by then setting a value for the newest date

I am currently making a program that will send an email if the date of a persons last entry was over (some time frame). my table is laid out like this:
employee | dept. | date | other |
bob | 1 | 2012-05-29 | abc |
bob | 1 | 2012-07-15 | xyz |
jon | 2 | 2012-05-29 | abc |
(i have sorted with mysql by employee then date)
so for example, for bob i want to automatically assign a variable to the date 2012-07-15 because that is the date of his last entry. then based on the current date i want to send an email if that time in between submissions has been to long. My question is how to i assign a variable to the last date of each person in the table? I am also open to different better ways of doing this. Thank you.
To return the latest date for each employee, something like this will work.
SELECT employee
, MAX(`date`) AS latest_date
FROM mytable
GROUP BY employee
ORDER BY employee
Addendum,
As simbabque points out, this works for getting the latest date, but does not return the other value. There are a couple of approaches to getting that result set.
If we are guaranteed that the (employee,date) is UNIQUE (for example. by a unique constraint), we can return other columns on the row that has the latest date with a query like this:
SELECT t.employee, t.`date`, t.other
FROM mytable t
JOIN ( SELECT r.employee, MAX(r.`date`) AS latest_date
FROM mytable r
GROUP BY r.employee
) s
ON s.employee = t.employee
AND s.latest_date = t.`date`
ORDER BY t.employee
If we aren't guaranteed that (employee, date) is unique, this query would not suffice. But there are a couple of approaches to addressing that issue.
Here's a solution in Perl. Credit for the SQL query goes to #spencer7593.
If you are not familiar with DBI I suggest you take a quick look. Also look at DBD::mysql to see how the datasource (DSN) is created.
You basically need to connect to the DB, prepare your query, execute it and fetch the results. You can then use them to send your email.
Here's a quick example that does not include the actual email sending:
use strict;
use warnings;
use DBI;
require 'script_that_has_custom_email_sub.pl'; # or use a module or whatever
# create the database handle
my $dbh = DBI->connect("DBI:mysql:database=test;host=localhost", # <-- DSN
'username', 'password')
or die $DBI::errstr;
# prepare the query to get a statement handle
my $sth = $dbh->prepare(<<__SQL__
SELECT employee
, MAX(`date`) AS latest_date
FROM mytable
GROUP BY employee
ORDER BY employee
__SQL__
);
$sth->execute; # send the query to the mysql server
# fetch each row of the result as a hashref
while (my $res = $sth->fetchrow_hashref) {
# access $res with the keys employee and latest_date from the query
# and send the mail
&custom_send_email_sub($res->{'employee'}, $res->{'latest_date'});
}
$sth->finish;

Query to find the difference between successive rows in Mysql

As I am new to MySQL, this question may be silly. How can I find the difference between successive rows?
Example:
A table (tableName='Abc') contains a single row as follows,
|DATA|
|10 |
|20 |
|30 |
|40 |
|50 |
Here I want get the output as,
|Dif|
|10 |
|10 |
|10 |
|10 |
How to find the difference like this without any index (primary or Auto_increment)?
A self-join is one way to compare consecutive rows:
SELECT
MIN(T2.DATA - T1.DATA) AS Dif
FROM
Abc T1 INNER JOIN Abc T2 on T1.DATA < T2.DATA
ORDER BY
T1.DATA
Use user-defined variables:
SET #prevValue:=0;
SELECT value-#prevValue AS diff,#prevValue:=value FROM t;
You'll have to discard the first row for your case.
If the table contains both value and diff fields, you can set the diff field with:
SET #prevValue:=0;
UPDATE t SET diff=IF((#d:=value-#prevValue) AND (#prevValue:=value),#d,#d);
Using the IF is the only way I could find to both set diff and update the #prevValue variable.
It would be best to do this outside of the DB by keeping track of the previous row. Here's some code (hopefully my php is not too rusty):
<?php
$prevNum = 0;
$db = mysql_connect(...); // fill in connection string
mysql_select_db("my_db", $db);
$result = mysql_query("select DATA from Abc order by DATA");
while ($row = mysql_fetch_array($result)) {
$diff = $row[0] - $prevNum;
echo "$diff\n";
$prevNum = $row[0];
}
?>
If you need to do it within the db for some reason then it would probably be best to create a stored procedure which will basically do the same thing: instantiate a variable with value 0, report the difference for each row and that variable, then set the variable to the row value.
Edited to add an order by clause as noted by John Pick

Inserting seconds into a database in HH:MM:SS format

I have a JavaScript timer on a PHP page. It returns a number, representing seconds, that I want to insert into a database column of type Time. How can I do that?
This code is the best I could find:
$query = "INSERT INTO #__quiz_r_student_question (c_stu_quiz_id,
c_question_id,
c_score,
c_attempts,
is_correct,
time_to_solve
)" .
"\n VALUES('".$stu_quiz_id."',
'".$quest_id."',
'".$c_quest_score."',
'".($c_quest_cur_attempt + 1)."',
'".$is_correct."',
DATEADD(ms, ".$time_to_solve."* 1000, 0)
)";
This answer was originally posted by the asker as an edit to the question.
I solved my problem with PHP, not an SQL query. I used this code to change my seconds value into a readable time ready for database insertion:
$time = gmdate('H:i:s',$time_to_solve)
You can use a SEC_TO_TIME function. Take a look at this script -
CREATE TABLE table_time(
column1 TIME DEFAULT NULL
);
INSERT INTO table_time VALUES(SEC_TO_TIME(3600));
SELECT * FROM table_time;
+----------+
| column1 |
+----------+
| 01:00:00 |
+----------+