why Zend_Db_Adapter is much slower than mysql_query - php

I use Zend Framework in my project. I need to insert multiple records and I found that Zend_Db suprisingly slower thatn my_sql query (several times), that made me think I did something wrong.
Here are two examples.
Zend_Db_Adapter:
$startTime = microtime(true);
$db = Zend_Db_Table::getDefaultAdapter();
$db->beginTransaction();
$dateAdded = date('Y-m-d H:i:s');
$lastChanged = $dateAdded;
foreach ($importDataNamespace->data as $subscriberNum => $subscriber)
{
foreach ($fieldsMap as $fieldNumber => $fieldTag) {
if (isset($subscriber[$fieldNumber])) {
$subscriberData[$fieldTag] = $subscriber[$fieldNumber];
} else {
$subscriberData[$fieldTag] = '';
}
}
$query = 'INSERT INTO subscribers (list_id, account_id, email_address, first_name, last_name, date_added, last_changed) ' .
'VALUES (' . 52 . ', ' . 29 . ', ' . $db->quote($subscriberData['EMAIL']) . ', ' . $db->quote($subscriberData['FNAME']) .
', ' . $db->quote($subscriberData['LNAME']) . ', ' . $db->quote($dateAdded) . ', ' . $db->quote($lastChanged) . ')';
$db->query($query);
}
$db->commit();
$this->view->time = microtime(true) - $startTime;
Example with mysql_query:
$startTime = microtime(true);
$user = 'root';
$password = 'password';
$db = 'database';
$connect = #mysql_connect('localhost',$user,$password) or die("Failed to connect database");
#mysql_select_db($db) or die("Failed to select database");
$dateAdded = date('Y-m-d H:i:s');
$lastChanged = $dateAdded;
$result = mysql_query('SET autocommit = 0');
foreach ($importDataNamespace->data as $subscriberNum => $subscriber)
{
foreach ($fieldsMap as $fieldNumber => $fieldTag) {
if (isset($subscriber[$fieldNumber])) {
$subscriberData[$fieldTag] = $subscriber[$fieldNumber];
} else {
$subscriberData[$fieldTag] = '';
}
}
$query = 'INSERT INTO subscribers (list_id, account_id, email_address, first_name, last_name, date_added, last_changed) ' .
'VALUES (' . 52 . ', ' . 29 . ', \'' . mysql_real_escape_string($subscriberData['EMAIL']) . '\', \'' . mysql_real_escape_string($subscriberData['FNAME']) .
'\', \'' . mysql_real_escape_string($subscriberData['LNAME']) . '\', \'' . $dateAdded . '\', \'' . $lastChanged . '\')';
mysql_query($query);
}
$result = mysql_query('SET autocommit = 1');
$result = mysql_query('COMMIT;');
$this->view->time = microtime(true) - $startTime;
In the first case it took 14.8 seconds, in the second 3.7.
Could you tell me why does it happend and what do you do wrong?
If I delete any quote for Zend_Db it took 12 seconds from 14 with quote, but it's still much more slower than with mysql_query:
$startTime = microtime(true);
$db = Zend_Db_Table::getDefaultAdapter();
$db->beginTransaction();
$dateAdded = date('Y-m-d H:i:s');
$lastChanged = $dateAdded;
foreach ($importDataNamespace->data as $subscriberNum => $subscriber)
{
foreach ($fieldsMap as $fieldNumber => $fieldTag) {
if (isset($subscriber[$fieldNumber])) {
$subscriberData[$fieldTag] = $subscriber[$fieldNumber];
} else {
$subscriberData[$fieldTag] = '';
}
}
$query = 'INSERT INTO subscribers (list_id, account_id, email_address, first_name, last_name, date_added, last_changed) ' .
'VALUES (' . 52 . ', ' . 29 . ', \'' . $subscriberData['EMAIL'] . '\', \'' . $subscriberData['FNAME'] .
'\', \'' . $subscriberData['LNAME'] . '\', \'' . $dateAdded . '\', \'' . $lastChanged . '\')';
$db->query($query);
}
$db->commit();
$this->view->time = microtime(true) - $startTime;
Thank you for any information about this issue.
This code takes about 0.065 seconds with mysql_query:
$dateAdded = date('Y-m-d H:i:s');
$lastChanged = $dateAdded;
$startTime = microtime(true);
$result = mysql_query('BEGIN');
for ($i = 0; $i < 100; $i++) {
$email = 'test_ ' . $i . '#gmail.com';
$query = 'INSERT INTO subscribers (list_id, account_id, email_address, first_name, last_name, date_added, last_changed) ' .
'VALUES (' . 52 . ', ' . 29 . ', \'' . mysql_real_escape_string($email) . '\', \'' . mysql_real_escape_string($firstName) .
'\', \'' . mysql_real_escape_string($lastName) . '\', \'' . mysql_real_escape_string($dateAdded) . '\', \'' . mysql_real_escape_string($lastChanged) . '\')';
mysql_query($query);
}
$result = mysql_query('COMMIT');
$time = microtime(true) - $startTime;
echo 'Using mysql_query: ' . $time . '<br />';
exit();
Code with benchmark of Zend_Db_Adapter (I didn't even use quote in this case):
$db = Zend_Db_Table::getDefaultAdapter();
$db->getProfiler()->setEnabled(true);
$profiler = $db->getProfiler();
$startTime = microtime(true);
$db->beginTransaction();
for ($i = 0; $i < 100; $i++)
{
$email = 'test_' . $i . '#gmail.com';
$query = 'INSERT INTO subscribers (list_id, account_id, email_address, first_name, last_name, date_added, last_changed) ' .
'VALUES (' . 52 . ', ' . 29 . ', \'' . $email . '\', \'' . $firstName .
'\', \'' . $lastName . '\', \'' . $dateAdded . '\', \'' . $lastChanged . '\')';
$db->query($query);
}
$db->commit();
$time = microtime(true) - $startTime;
echo 'Time of transaction Zend_Db_Adapter query: ' . $time . '<br />';
echo 'Total time ' . $profiler->getTotalElapsedSecs() . '<br />';
$count = 0;
$totalTime = 0;
foreach ($profiler->getQueryProfiles() as $query) {
$count++;
$elapsedTime = $query->getElapsedSecs();
$totalTime += $elapsedTime;
echo $count . ' ' . $elapsedTime . ' ' . $query->getQuery() . '<br />';
}
echo 'Sum time: ' . $totalTime . '<br />';
Here are some results:
Time of transaction Zend_Db_Adapter query: 0.23094701767
Total time 0.0949234962463
1 0.00199699401855 connect
2 0.000336885452271 begin
3 0.000540018081665 INSERT INTO subscribers (list_id, account_id, email_address, first_name, last_name, date_added, last_changed) VALUES (52, 29, 'test_0#gmail.com', 'John', 'Clinton', '2011-01-28 15:25:21', '2011-01-28 15:25:21')
4 0.000504016876221 INSERT INTO subscribers (list_id, account_id, email_address, first_name, last_name, date_added, last_changed) VALUES (52, 29, 'test_1#gmail.com', 'John', 'Clinton', '2011-01-28 15:25:21', '2011-01-28 15:25:21')
It's very strange. The time of transacation to insert 100 records is 2.5 times more than executing of all queries.
If I try to meach the time of forming strings in the loop it (if we delete query) doesn't take so much time.

I think that one reason is that you execute $db->quote() too many times, which is unnecessary. Do you know that $db->quote() can take an array as its argument and you can basically reduce calls to $db->quote() to only one. In addition in your mysql_query version you do not escape $dateAdded and $lastChanged, while in zend_db version you do.
EDIT: Added an example below
$db = Zend_Db_Table::getDefaultAdapter();
$input = array(
'a' => "asd'lfksd",
'b' => "asdlfk'sdfasdf",
'c' => "asd fds f saf'sdfsd",
'd' => "asd fds f saf'sdfsd"
);
// separate calls to quote
$startTime = microtime(true);
$db->quote($input['a']);
$db->quote($input['b']);
$db->quote($input['c']);
$db->quote($input['d']);
$totalTime1 = microtime(true) - $startTime;
// one call to quote
$startTime = microtime(true);
$db->quote($input);
$totalTime2 = microtime(true) - $startTime;
// show results
var_dump("Sperate calls are ". $totalTime1/$totalTime2 . " times slower");
//output: string 'Sperate calls are 3.0875831485588 times slower' (length=46)

mysql_* functions are natives to PHP , so they are very fast.
Zend_Db_Adapter is working with PDO so you have a first absraction layer with PDO and a second with Zend_DB_Adapter.
More layer abstraction you 've got , more the code is slowing down.
That why the MVC framework in general are more slower than procedural code.
Try again your benchmark with prepared statement and a cache mechanism and you should be close of the mysql_* functions

This looks like you don't have a metadata cache in Zend_Db.

Related

PHP trim not working

I m trying to get rid of the trailing comma my sql statemen using trim but it doesnt seem to work
if(isset($_POST['report'])){
//====
$sql = 'INSERT INTO weekly_repo ( hospnme, disease, week, under5, above5, dat) VALUES ';
$week = intval($_POST['current_week']);
$diseases = $_POST['diseases'];
foreach($diseases as $disease){
$sql .= ' ( "' . filterString($_POST['hospital']). '", "' . filterString($disease['disease']) . '", ' . $week . ', ' . intval($disease['under_5']) . ', ' . intval($disease['over_5']) . ', NOW()), ';
}
$sql = trim($sql, '\,');
// ended up doing this
//$sql = substr($sql, 0, strrpos($sql, ',')) . ';';
$stmt = $conn->query($sql);
}
You don't need to escape \ the comma. A better approach might be to construct an array and then implode():
foreach($diseases as $disease){
$sql[] = ' ( "' . filterString($_POST['hospital']). '", "' . filterString($disease['disease']) . '", ' . $week . ', ' . intval($disease['under_5']) . ', ' . intval($disease['over_5']) . ', NOW()), ';
}
$sql = implode(',', $sql);

Date is not saving to database

I'm creating an appointment plugin, in that for already members and new members are there. in already member area the date entry is not entring into db. but when displaying the variale contains the date. i'm have been checking this from morning but didn't get what is the error. my code is:
$source = mysql_real_escape_string(trim($_POST['apdatetime']));
$datetime = explode(',', $source);
$dates = $datetime[0];
$app_time = $datetime[1];
if($app_time < 12){
$app_session = 'am';
}
else{
$app_session ='pm';
}
$splitdatet = explode('/', $dates);
$yyear = $splitdatet[2];
$mmonth = $splitdatet[1];
$ddate = $splitdatet[0];
$app_date = $yyear . "-" . $mmonth . "-" . $ddate;
if ($_POST['isnewpatient'] == "false") {
//$cSql = "select * from " . WP_contact . " where appointments_c_patientid='" . trim($_POST['ptntid']) . "' ";
$cSql = "select * from " . WP_eemail_TABLE_SUB . " where eemail_patient_id='" . trim($_POST['ptntid']) . "' ";
$data = $wpdb->get_results($cSql);
if (empty($data )) {
$err = 1;
echo "<div id='message' class='aerror'>No such patient ID exists....</div>";
} else {
#$mobile = htmlspecialchars(stripslashes($data[0]->eemail_mobile_sub));
#$email = htmlspecialchars(stripslashes($data[0]->eemail_email_sub));
#$name = htmlspecialchars(stripslashes($data[0]->eemail_name_sub));
$sqlss = "insert into " . WP_Appointments .
" (`appointments_patient_id`,`appointments_date`,`appointments_time`,`appointments_session`,`appointments_reg_date`) VALUES ('" .
mysql_real_escape_string(trim($_POST['ptntid'])) . "','" .
$app_date . "','" .
$app_time . "','" .
$app_session . "',CURRENT_TIMESTAMP() )";
$dd=$wpdb->get_results($sqlss);
var_dump($dd);
echo 'Date:'.$app_date;
// return $suc;
echo "<div id='message' class='asuccess' >Request has been sent for appointment</div>";
}
}
The output of the var_dump is array[0] and $app_date is Date:2014-10-22
The db entry is
appointments_id :393
appointments_patient_id : 9999999999
appointments_date : 0000-00-00
appointments_time : 9:00
appointments_session : am
appointments_reg_date : 2014-09-25 14:21:35
could anyone please point out the mistake in the code if any??
You should convert your string into date object like this:
$app_date = date('Y-m-d',strtotime($app_date));

PHP Mysql multiple inserts (big dataset)

I have been running a relatively simple script, however it tends to lock up the database.
Each time a lookup value is inserted, it is also checked against to make sure it isnt inserted again.
This works very well on small data sets (<50k), however it has issues with large data sets (>2m). Any help would be appreciated.
$insertCounter = 20;
$matchCounter = 200;
$insertIndex = 0;
$sqlInsert = 'INSERT INTO `database` (`lookup_value`, `timestamp`, `source`) VALUES ';
$matchIndex = 0;
$resetCount = 0;
$indexCounter = 0;
foreach ($matches as $lookup) {
$sqlSelect = 'SELECT `id` FROM `database` WHERE `lookup_value` = \'' . $lookup . '\'';
$qExisting = ExecuteSQL($sqlSelect);
if (mysql_num_rows($qExisting) == 0) {
$insertIndex += 1;
$sqlInsert .= '(\'' . strtolower($lookup) . '\', \'' . date('Y-m-d H:i:s') . '\', \'database\'), ';
if ($insertIndex >= $insertCounter) {
$sqlInsert = substr($sqlInsert, 0, strlen($sqlInsert) - 2);
ExecuteSQLNoResult($sqlInsert);
echo '<p><strong>' . date('Y-m-d H:i:s') . '</strong><br />' . $sqlInsert . '</p>';
$sqlInsert = 'INSERT INTO `database` (`lookup_value`, `timestamp`, `source`) VALUES ';
$insertIndex = 0;
}
}
mysql_free_result($qExisting);
$matchIndex += 1;
$indexCounter += 1;
if ($matchIndex > $matchCounter) {
$resetCount += 1;
$matchIndex = 0;
echo '<p>(' . $indexCounter . ', reset no.' . $resetCount . ') Counter Reached, resetting.</p>';
}
}
How about adding a UNIQUE index with lookup_value field and then using INSERT IGNORE statement?

php, How to set date to a month?

I have a calendar and I'm adding recurring events to it, My problem is that the user can set a "repeat for x amount of months" variable, this variable is my $repeat_value and if it exceeds 12 i need to handle it in a way that the date year increases by 1, and i should set the month to 1, then add remaining months... how can i do this?
my code is setup like this:
for ($i = 1; $i < $repeat_value; $i++)
{
if ($repeat_type == "month")
{
if ($i > 12) //month is greater then 12, we must increase the year +1 and set month to jan
{
//please help!
}
else
{
$t_temp_start = date('Y-m-d h:m:s',strtotime($s_start . '+ ' . $i . ' months'));
$t_temp_end = date('Y-m-d h:m:s',strtotime($s_end . '+ ' . $i . ' months'));
}
$database->justquery("INSERT INTO tbl_events (`title`, `start`, `end`, `url`) VALUES ('" . $event_name . "', '" . $t_temp_start . "', '" . $t_temp_end . "', '" . $url . "');");
}
else if ($repeat_type == "year")
{
$t_temp_start = date('Y-m-d h:m:s',strtotime($s_start . '+ ' . $i . ' years'));
$t_temp_end = date('Y-m-d h:m:s',strtotime($s_end . '+ ' . $i . ' years'));
$database->justquery("INSERT INTO tbl_events (`title`, `start`, `end`, `url`) VALUES ('" . $event_name . "', '" . $t_temp_start . "', '" . $t_temp_end . "', '" . $url . "');");
}
}
I hope this makes sense, help appreciated!
The fact that there is more than 12 months has absolutely no importance.
Simply put:
for ($i = 1; $i < $repeat_value; $i++)
{
if ($repeat_type == "month")
{
$t_temp_start = date('Y-m-d h:m:s',strtotime($s_start . '+ ' . $i . ' months'));
$t_temp_end = date('Y-m-d h:m:s',strtotime($s_end . '+ ' . $i . ' months'));
$database->justquery("INSERT INTO tbl_events (`title`, `start`, `end`, `url`) VALUES ('" . $event_name . "', '" . $t_temp_start . "', '" . $t_temp_end . "', '" . $url . "');");
}
else if ($repeat_type == "year")
{
$t_temp_start = date('Y-m-d h:m:s',strtotime($s_start . '+ ' . $i . ' years'));
$t_temp_end = date('Y-m-d h:m:s',strtotime($s_end . '+ ' . $i . ' years'));
$database->justquery("INSERT INTO tbl_events (`title`, `start`, `end`, `url`) VALUES ('" . $event_name . "', '" . $t_temp_start . "', '" . $t_temp_end . "', '" . $url . "');");
}
}
You can try this:
// these two lines produce exactly the same result
print date('Y-m-d h:m:s',strtotime('today + 14 months'));
print date('Y-m-d h:m:s',strtotime('today + 1 year + 2 months'));
Also, I thought you could try this simpler/shorter way of doing what you want:
$db_query = '';
for ($i = 1; $i < $repeat_value; $i++)
{
$t_temp_start = data('Y-m-d h:m:s', strtotime($s_start.' + '.$i.' '.$repeat_type.'s'));
$t_temp_end = $t_temp_start + $s_end - $s_start;
db_query .= "INSERT INTO tbl_events (`title`, `start`, `end`, `url`) VALUES ('" . $event_name . "', '" . $t_temp_start . "', '" . $t_temp_end . "', '" . $url . "');";
}
if (!empty($db_query))
$database->justquery($db_query);
Which potentially saves you lots of database queries (which should never be placed inside loops), and makes your code easier to read (by saving you a conditional statement). It also enables you to accept repeats each year, month, but also week, day, hour and so on without changing a single byte of your code.
In a more general point of view, you should notice when you have an if() ... else conditional statement where the else is almost exactly a repetition of the if. It is often a sign that there is room for improvement in your code.
And one last word: be careful to SQL injections.

Filtering MYSQL results by date

We have a script that generate invoice once a month (cron). But we would like to add feature, that we would be able to select date range "from - to" and then generate invoice only for the date selected.
I guess making input fields with calendar pop-up isn't hard, but filtering with PHP is a bit bigger challenge, so if anyone want to take a look at my code and give me some tips, I would be grateful.
function genInvoice($clientID, $orderID=0, $paid=false)
{
if($orderID == 0)
$sql = "select tblorders.* from tblorders,tblusers where invoiceid=0 and tblorders.userid=tblusers.id " .
"and status='closed' and tblusers.clientid=" . $clientID;
else
$sql = "select tblorders.* from tblorders,tblusers where invoiceid=0 and tblorders.userid=tblusers.id " .
"and tblusers.clientid=" . $clientID . " and tblorders.id=" . $orderID;
$res = full_query($sql) or die(mysql_error());
// If no closed orders uninvoiced, just return
if(!mysql_num_rows($res))
return 0;
$amount = 0;
$orders = array();
while($row = mysql_fetch_array($res, MYSQL_ASSOC))
{
// print_r($row);
// print "<br><hr>";
$amount += $row['amount'];
$orders[] = $row['id'];
}
$date = date("Y-m-d");
$status = $paid ?'Paid' : 'Unpaid';
$sql = "insert into tblinvoices (clientid, date, duedate, subtotal, total, status) values (" . $clientID . ",'" . $date .
"','" . $date . "'," . $amount . "," . $amount . ",'" . $status . "')";
$res = full_query($sql) or die(mysql_error());
$invoiceid = mysql_insert_id();
$sql = "update tblorders set invoiceid=" . $invoiceid . " where id in (" . implode(",", $orders) . ")";
$res = full_query($sql) or die(mysql_error());
$sql = "select tblorders.id as ReportID, FirstName, LastName, SearchName, CountyID, StateID, bl_orderitems.userid, bl_orderitems.amount, " .
"bl_orderitems.notes from tblorders, bl_orderitems left join bl_search on bl_search.id=packageid where tblorders.id in (" .
implode(",", $orders) . ") and bl_orderitems.orderid=tblorders.id order by tblorders.id,bl_orderitems.id";
$res = full_query($sql) or die(mysql_error());
while($row = mysql_fetch_array($res, MYSQL_ASSOC))
{
if($row['CountyID'] != 0)
$locale = getCounty($row['CountyID']);
else if($row['StateID'] != 0)
$locale = getState($row['StateID']);
if($row['SearchName'] != "")
$description = mysql_real_escape_string($row['FirstName'] . " " . $row['LastName'] . " " .
$row['SearchName'] . " " . $locale . " (Order #" . $row['ReportID'] . ")");
else
$description = "Search Package: " . mysql_real_escape_string($row['notes'] . " (Order #" . $row['ReportID'] . ")");
$sql = "insert into tblinvoiceitems (invoiceid, userid, type, description, amount, duedate) values " .
"(" . $invoiceid . "," . $row['userid'] . ",'search','" . $description . "','" .
$row['amount'] . "','" . $date . "')";
// print $sql . "<br>";
full_query($sql) or die(mysql_error());
}
sendmessage ('Invoice Created', $invoiceid);
return $invoiceid;
}
not going to look through all that code, but filtering results by a date range is easy.
SELECT id FROM some_table WHERE some_date_field BETWEEN $first_date AND $second_date

Categories