Below is my working query that gets into action after a form submit. My query works only if all textboxes are filled, so currently everything is required.
WORKING QUERY
SELECT behaviour.hash,
Sum(behaviour.timespent) AS timeSpent,
new_table.percentile_rank,
Count(*) AS total
FROM behaviour,
audience,
new_table
WHERE ( $url )
AND behaviour.timestamp >= Date_sub(Curdate(), INTERVAL $last_visit day) AND behaviour.timestamp < Date_add(Curdate(), INTERVAL 1 day)
AND behaviour.hash = audience.hash
AND behaviour.hash = new_table.hash
AND audience.country = '$from_country'
GROUP BY behaviour.hash
HAVING Count(*) >= $more_than
AND timespent >= $time_spent
AND new_table.percentile_rank >= $lead_scoring
What I want to achieve is not to require the user to fill all the textboxes in order to submit, but only those he prefers. So I built the following, but it has some errors.
My problem is that my query has a having clause so not every condition is connected with AND as I have for now (look below my code). So, the first of the $more_than or $time_spent or $lead_scoring textboxes that submitted, it must have HAVING instead of AND.
How to edit my code in order to achieve this "special conditions" ?
MY CODE
$url= 'url="'.implode('" OR url="', $vals).'"';
$conditions = array();
if (!empty($last_visit)) $conditions[] = "behaviour.TIMESTAMP >= DATE_SUB( CURDATE( ) , INTERVAL '".$last_visit."' DAY) AND behaviour.TIMESTAMP < DATE_ADD( CURDATE( ) , INTERVAL 1 DAY ) ";
if (!empty($from_country)) $conditions[] = "audience.country = '".$from_country."'";
if (!empty($more_than)) $conditions[] = "COUNT( * ) >= '".$more_than."'";
if (!empty($time_spent)) $conditions[] = "timeSpent >= '".$time_spent."'";
if (!empty($lead_scoring)) $conditions[] = "new_table.percentile_rank >= '".$lead_scoring."'";
$conditionString = implode(' AND ', $conditions);
$sql = "SELECT behaviour.hash,
Sum(behaviour.timespent) AS timeSpent,
new_table.percentile_rank,
Count( * ) AS total
FROM behaviour,
audience,
new_table
WHERE ($url) AND ".$conditionString;
CURRENT OUTPUT
In the example below, all textboxes except more_than have been filled. The thing is that instead AND timespent >= '20' should be HAVING timespent >= '20'
SELECT behaviour.hash,
SUM(behaviour.timespent) AS timeSpent,
new_table.percentile_rank,
Count(*) AS total
FROM behaviour,
audience,
new_table
WHERE ( url = "/10369" )
AND behaviour.timestamp >= Date_sub(Curdate(), interval '3' day)
AND behaviour.timestamp < Date_add(Curdate(), interval 1 day)
[missing] AND behaviour.hash = audience.hash
[missing] AND behaviour.hash = new_table.hash
AND audience.country = 'it'
[missing] GROUP BY behaviour.hash
[wrong] AND timespent >= '20' ////// it should be HAVING /////
AND new_table.percentile_rank >= '30'
First, you must ensure SQL injection is not possible. To do that, lets use PDO.
Next, to solve your actual problem, you simple need to create two lists with conditions. One with the conditions you want to have in the WHERE part of the query, and one with the conditions that need to go in the HAVING part of the query.
$pdo = new PDO(/* See http://php.net/manual/en/pdo.construct.php */);
$whereConditions = [];
$havingConditions = [];
$parameters = [];
if (!empty($last_visit)) {
$whereConditions[] = "behaviour.TIMESTAMP >= DATE_SUB( CURDATE( ) , INTERVAL :last_visit DAY) AND behaviour.TIMESTAMP < DATE_ADD( CURDATE( ) , INTERVAL 1 DAY ) ";
$parameters['last_visit'] = $last_visit;
}
if (!empty($from_country)) {
$whereConditions[] = "audience.country = :from_country";
$parameters['from_country'] = $from_country;
}
if (!empty($more_than)) {
$havingConditions[] = "COUNT( * ) >= :more_than";
$parameters['more_than'] = $more_than;
}
if (!empty($time_spent)) {
$havingConditions[] = "timeSpent >= :time_spent";
$parameters['time_spent'] = $time_spent;
}
if (!empty($lead_scoring)) {
$havingConditions[] = "new_table.percentile_rank >= :lead_scoring";
$parameters['lead_scoring'] = $lead_scoring;
}
if (count($vals)) {
$escapedUrlList = implode(', ', array_map(function ($url) use ($pdo) {
return $pdo->quote($url);
}, $vals));
$whereConditions[] = "url IN($escapedUrlList)";
}
$whereClause = count($whereConditions) ? ' AND ' . implode(' AND ', $whereConditions) : '';
$havingClause = count($havingConditions) ? ' HAVING ' . implode(' AND ', $havingConditions) : '';
$statement = $pdo->prepare("
SELECT behaviour.hash,
Sum(behaviour.timespent) AS timeSpent,
new_table.percentile_rank,
Count(*) AS total
FROM behaviour,
audience,
new_table
WHERE behaviour.hash = audience.hash
AND behaviour.hash = new_table.hash
{$whereClause}
GROUP BY behaviour.hash
{$havingClause}
");
$result = $statement->execute($parameters);
Here's a bit "tricky" approach (looks clean though) that is using prepared statements. I've added some general purpose "features" in case of future changes.
Read the comments with explanations (will be more convenient this way I think):
//assume established PDO connection - example:
try {
$pdo = new PDO("mysql:dbname={$database_name};host=localhost", $user, $password);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
//static: conditional strings without parameters check (no keys required)
//conditional: assoc - keys should match both query placeholders and variable names
$static_where = [];
$optional_where = [
'last_visit' => 'behaviour.TIMESTAMP >= DATE_SUB(CURDATE(), INTERVAL :last_visit DAY) AND behaviour.TIMESTAMP < DATE_ADD(CURDATE(), INTERVAL 1 DAY)',
'from_country' => 'audience.country = :from_country'
];
$static_having = [];
$optional_having = [
'more_than' => 'COUNT(*) >= :more_than',
'time_spent' => 'timeSpent >= :time_spent',
'lead_scoring' => 'new_table.percentile_rank >= :lead_scoring'
];
//params: query parameters array - assigned manually + resolved from optional variables
$params = [];
//resolve condition from $urls array
if (count($urls) == 1) {
$static_where[] = 'url = :url';
$params['url'] = reset($urls);
} else if (!empty($urls)) {
foreach ($urls as $idx => $url) {
$params['url' . $idx] = $url;
}
$static_where[] = 'url IN(:' . implode(', :', array_keys($params)) . ')';
}
//filtering existing params used in query
//empty() is not a good idea for general purpose though,
//because some valid values might be recognised as empty (int 0, string '0')
$params += array_filter(
compact(array_keys($optional_where), array_keys($optional_having)),
function ($value) { return !empty($value); }
);
//concatenating conditional strings
//with corresponding params that weren't filtered out
//or these without params (static)
$where_clause = implode(' AND ', $static_where + array_intersect_key($optional_where, $params));
$having_clause = implode(' AND ', $static_having + array_intersect_key($optional_having, $params));
//don't need clauses without conditions - same as if (!empty($where)) {...}
empty($where_clause) or $where_clause = 'WHERE ' . $where_clause;
empty($having_clause) or $having_clause = 'HAVING ' . $having_clause;
$sql = "SELECT
behaviour.hash,
Sum(behaviour.timespent) AS timeSpent,
new_table.percentile_rank,
Count( * ) AS total
FROM behaviour,
INNER JOIN audience ON behaviour.hash = audience.hash,
INNER JOIN new_table ON behaviour.hash = new_table.hash
{$where_clause}
GROUP BY behaviour.hash
{$having_clause}";
//PDO part
$query = $pdo->prepare($sql);
$result = $query->execute($params);
//...
Here is a less complicated way using string concatenation instead of implode. The "trick" is to start the conditions with 1=1. So every following condition can begin with AND.
$andWhere = '';
$andHaving = '';
$params = [];
if (!empty($last_visit)) {
$andWhere .= " AND behaviour.TIMESTAMP >= CURDATE() - INTERVAL :last_visit DAY AND behaviour.TIMESTAMP < CURDATE() + INTERVAL 1 DAY";
$params['last_visit'] = $last_visit;
}
if (!empty($from_country)) {
$andWhere .= " AND audience.country = :from_country";
$params['from_country'] = $from_country;
}
if (!empty($more_than)) {
$andHaving .= " AND COUNT( * ) >= :more_than";
$params['more_than'] = $more_than;
}
if (!empty($time_spent)) {
$andHaving .= " AND timeSpent >= :time_spent";
$params['time_spent'] = $time_spent;
}
if (!empty($lead_scoring)) {
$andHaving .= " AND new_table.percentile_rank >= :lead_scoring";
$params['lead_scoring'] = $lead_scoring;
}
$urlPlaceholders = [];
foreach ($vals as $key => $val) {
$urlPlaceholders[] = ":url_$key";
$params["url_$key"] = $val;
}
if (count($vals) > 0) {
$inUrl = implode(',', $urlPlaceholders);
$andWhere .= " AND url IN ($inUrl)";
}
$sql = "
SELECT behaviour.hash,
Sum(behaviour.timespent) AS timeSpent,
new_table.percentile_rank,
Count(*) AS total
FROM behaviour
JOIN audience ON behaviour.hash = audience.hash
JOIN new_table ON behaviour.hash = new_table.hash
WHERE 1=1 {$andWhere}
GROUP BY behaviour.hash
HAVING 1=1 {$andHaving}
";
#var_export($sql);
#var_export($params);
$sth = $dbh->prepare($sql);
$sth->execute($params);
$data = $sth->fetchAll(PDO::FETCH_ASSOC);
#var_export($data);
Having sample data like
$last_visit = '';
$from_country = 'UK';
$more_than = '5';
$time_spent = '3';
$lead_scoring = '';
$vals = ['u1', 'u2'];
You would get the following query:
SELECT behaviour.hash,
Sum(behaviour.timespent) AS timeSpent,
new_table.percentile_rank,
Count(*) AS total
FROM behaviour
JOIN audience ON behaviour.hash = audience.hash
JOIN new_table ON behaviour.hash = new_table.hash
WHERE 1=1 AND audience.country = :from_country AND url IN (:url_0,:url_1)
GROUP BY behaviour.hash
HAVING 1=1 AND COUNT(*) >= :more_than AND timeSpent >= :time_spent
with these bindings:
array (
'from_country' => 'UK',
'more_than' => '5',
'time_spent' => '3',
'url_0' => 'u1',
'url_1' => 'u2',
)
Demo on rextester.com
If having is the only problem why not splitting it into different block like this:
$conditions = array();
if (!empty($last_visit)) $conditions[] = "behaviour.TIMESTAMP >= DATE_SUB( CURDATE( ) , INTERVAL '".$last_visit."' DAY) AND behaviour.TIMESTAMP < DATE_ADD( CURDATE( ) , INTERVAL 1 DAY ) ";
if (!empty($from_country)) $conditions[] = "audience.country = '".$from_country."'";
$conditionString = implode(' AND ', $conditions);
$conditions_having = array();
if (!empty($more_than)) $conditions_having[] = "COUNT( * ) >= '".$more_than."'";
if (!empty($time_spent)) $conditions_having[] = "timeSpent >= '".$time_spent."'";
if (!empty($lead_scoring)) $conditions_having[] = "new_table.percentile_rank >= '".$lead_scoring."'";
$conditionString .= " GROUP BY behaviour.hash"
if(count($conditions_having))
$conditionString .= " HAVING ".implode(' AND ', $conditions_having);
$sql = "SELECT behaviour.hash,
Sum(behaviour.timespent) AS timeSpent,
new_table.percentile_rank,
Count( * ) AS total
FROM behaviour,
audience,
new_table
WHERE ($url) AND ".$conditionString;
You can change only your implode function to this code
$conditionString = implode(' ', array_map(function($item) {
if ((strpos($item, 'timeSpent') !== false))
return 'HAVING '.$item;
return 'AND '.$item;
}, $conditions));
Be aware that your code is vulnerable.
for more information see this: SQL Injection In CAPEC
$url= 'url="'.implode('" OR url="', $vals).'"';
$conditions = array();
$havings = array();
if (!empty($last_visit)) $conditions[] = "behaviour.TIMESTAMP >= DATE_SUB( CURDATE( ) , INTERVAL '".$last_visit."' DAY) AND behaviour.TIMESTAMP < DATE_ADD( CURDATE( ) , INTERVAL 1 DAY ) ";
if (!empty($from_country)) $conditions[] = "audience.country = '".$from_country."'";
if (!empty($more_than)) $havings[] = "COUNT( * ) >= '".$more_than."'";
if (!empty($time_spent)) $havings[] = "timeSpent >= '".$time_spent."'";
if (!empty($lead_scoring)) $havings[] = "new_table.percentile_rank >= '".$lead_scoring."'";
$conditionString = implode(' AND ', $conditions);
$havingString = '';
if(count($havings)>0) {
$havingString = ' having '.implode(', ', $havings);
}
$sql = "SELECT behaviour.hash,
Sum(behaviour.timespent) AS timeSpent,
new_table.percentile_rank,
Count( * ) AS total
FROM behaviour,
audience,
new_table
WHERE ($url) AND ".$conditionString.$havingString;
Related
This question already has answers here:
How can I sort arrays and data in PHP?
(14 answers)
Sort array of objects by one property
(23 answers)
Closed 4 years ago.
I am trying to sort an array from high to low.
The query is written in an Joomla module and is this one:
$authors = array();
if (count($rows))
{
foreach ($rows as $row)
{
$author = JFactory::getUser($row->created_by);
$author->link = JRoute::_(K2HelperRoute::getUserRoute($author->id));
$query = "SELECT id, gender, description, image, url, `group`, plugins FROM #__k2_users WHERE userID=".(int)$author->id;
$db->setQuery($query);
$author->profile = $db->loadObject();
if ($params->get('authorAvatar'))
{
$author->avatar = K2HelperUtilities::getAvatar($author->id, $author->email, $componentParams->get('userImageWidth'));
}
if (K2_JVERSION != '15')
{
$languageCheck = '';
if ($application->getLanguageFilter())
{
$languageTag = JFactory::getLanguage()->getTag();
$languageCheck = "AND i.language IN (".$db->Quote($languageTag).", ".$db->Quote('*').") AND c.language IN (".$db->Quote($languageTag).", ".$db->Quote('*').")";
}
$query = "SELECT i.*, c.alias as categoryalias FROM #__k2_items as i
LEFT JOIN #__k2_categories c ON c.id = i.catid
WHERE i.created_by = ".(int)$author->id."
AND i.published = 1
AND i.access IN(".implode(',', $user->getAuthorisedViewLevels()).")
AND ( i.publish_up = ".$db->Quote($nullDate)." OR i.publish_up <= ".$db->Quote($now)." )
AND ( i.publish_down = ".$db->Quote($nullDate)." OR i.publish_down >= ".$db->Quote($now)." )
AND i.trash = 0 AND created_by_alias='' AND c.published = 1 AND c.access IN(".implode(',', $user->getAuthorisedViewLevels()).") AND c.trash = 0 {$languageCheck} ORDER BY created DESC";
}
else
{
$query = "SELECT i.*, c.alias as categoryalias FROM #__k2_items as i
LEFT JOIN #__k2_categories c ON c.id = i.catid
WHERE i.created_by = ".(int)$author->id."
AND i.published = 1
AND i.access <= {$aid}
AND ( i.publish_up = ".$db->Quote($nullDate)." OR i.publish_up <= ".$db->Quote($now)." )
AND ( i.publish_down = ".$db->Quote($nullDate)." OR i.publish_down >= ".$db->Quote($now)." )
AND i.trash = 0 AND created_by_alias='' AND c.published = 1 AND c.access <= {$aid} AND c.trash = 0 ORDER BY created DESC";
}
$db->setQuery($query, 0, 1);
$author->latest = $db->loadObject();
$author->latest->id = (int)$author->latest->id;
$author->latest->link = urldecode(JRoute::_(K2HelperRoute::getItemRoute($author->latest->id.':'.urlencode($author->latest->alias), $author->latest->catid.':'.urlencode($author->latest->categoryalias))));
$query = "SELECT COUNT(*) FROM #__k2_comments WHERE published=1 AND itemID={$author->latest->id}";
$db->setQuery($query);
$author->latest->numOfComments = $db->loadResult();
if ($params->get('authorItemsCounter'))
{
if (K2_JVERSION != '15')
{
$languageCheck = '';
if ($application->getLanguageFilter())
{
$languageTag = JFactory::getLanguage()->getTag();
$languageCheck = "AND language IN (".$db->Quote($languageTag).", ".$db->Quote('*').")";
}
$query = "SELECT COUNT(*) FROM #__k2_items WHERE {$where} published=1 AND ( publish_up = ".$db->Quote($nullDate)." OR publish_up <= ".$db->Quote($now)." ) AND ( publish_down = ".$db->Quote($nullDate)." OR publish_down >= ".$db->Quote($now)." ) AND trash=0 AND access IN(".implode(',', $user->getAuthorisedViewLevels()).") AND created_by_alias='' AND created_by={$row->created_by} {$languageCheck} AND EXISTS (SELECT * FROM #__k2_categories WHERE id= #__k2_items.catid AND published=1 AND trash=0 AND access IN(".implode(',', $user->getAuthorisedViewLevels()).") {$languageCheck} )";
}
else
{
$query = "SELECT COUNT(*) FROM #__k2_items WHERE {$where} published=1 AND ( publish_up = ".$db->Quote($nullDate)." OR publish_up <= ".$db->Quote($now)." ) AND ( publish_down = ".$db->Quote($nullDate)." OR publish_down >= ".$db->Quote($now)." ) AND trash=0 AND access<={$aid} AND created_by_alias='' AND created_by={$row->created_by} AND EXISTS (SELECT * FROM #__k2_categories WHERE id= #__k2_items.catid AND published=1 AND trash=0 AND access<={$aid} ) ";
}
$db->setQuery($query);
$numofitems = $db->loadResult();
$author->items = $numofitems;
}
$authors[] = $author;
}
}
return $authors;
}
then the result is passing to these variables and finally authors table is returned. This table I want to sort from high to low. I have tried the php functions but none of them seem to working.
I cannot be sure if the query retrieves something else beyond the items counter and set it as key. But I have tried functions such as arsort which is sorting by value and not key.
$db->setQuery($query);
$numofitems = $db->loadResult();
$author->items = $numofitems;
$authors[] = $author;
arsort($authors);
Any idea is appreciated
Thank you
Generally database queries can be sorted by specifying an order as in
$query->order('my_table.my_column desc')
If you're sorting a PHP array and require a custom sort criteria, you can use your own sort criteria with usort. For example:
// Your comparison function:
function myComparer($object1, $object2)
{
if ($object1->item == $object2->item) {
return 0;
}
return ($object1->item < $object2-item) ? -1 : 1;
}
// Sort the array in place
usort($authors, 'myComparer');
Note you can reverse the order of the sort by simply swapping the comparison return values:
return ($object1->itemCount < $object2-itemCount) ? 1 : -1;
I am using that code without any problem via non-pdo.
mysql_query("
SELECT * FROM table
WHERE date_start >= '$date_start'
and date_end <= '$date_end'
");
But when I try using with pdo, code didn't work.
$query = $this->db->prepare("
SELECT * FROM table
WHERE date_start >= :date_start
and date_end <= :date_end
");
$query->execute(array(
'date_start'=>'2017-05-01 00:00',
'date_end'=>'2017-05-30 00:00'
));
Can I get help? Where is problem?
MORE INFO : (EDITED)
System : Windows (Appserv)
PHP Version : 5.6.30
I didn't see any error in error.log file.
I guess problem coming from $params array. Because worked like this:
$query = $db->prepare("SELECT * FROM brands WHERE brand_status=:brand_status and date_start >= :date_start and date_end <= :date_end");
$query->execute(array('brand_status'=>$brand_status, 'date_start'=>$date_s, 'date_end'=>$date_e));
But when I used execute(array($params)) not working. Actually if I not use date parameters working, but when I use date parameters not working. My example code below:
$extra = '';
$params = array();
$sql = "SELECT * FROM brands ORDER BY brand_name";
$brand_status = "1";
$brand_name = "";
$date_start = "2017-05-01 00:00:00";
$date_end = "2017-06-30 23:59:59";
if ($brand_status) {
$extra .= "brand_status=:brand_status and ";
$params[] = array("brand_status" => $brand_status);
}
if ($brand_name) {
$extra .= "brand_name LIKE :brand_name and ";
$params[] = array("brand_name" => "%" . $brand_name . "%");
}
if (dbDate($date_start)==1 and dbDate($date_end)==1)
{
$extra .= "date_start >= :date_start and date_end <= :date_end and ";
//problem here:
$params['date_start'] = $date_start;
$params['date_end'] = $date_end;
}
if (count($params) > 0) {
if (strlen($extra) > 0) {
$extra = rtrim($extra, ' and ');
}
$sql = "SELECT * FROM brands WHERE $extra ORDER BY brand_name";
$query = $db->prepare($sql);
$result = $query->execute($params);
} else {
$result = $query = $db->query($sql);
}
if($result)
$num = $query->rowCount();
if($num > 0) {
while($row = $query->fetch()) {
echo $row['brand_name'] . "<br>";
}
}
function dbDate($value)
{
$pattern = "/^1|2[0-9]{3}\-(0[1-9]|1[0-2])\-(0[1-9]|[1-2][0-9]|3[0-1])( [0-9]{2}:[0-9]{2}(:[0-9]{2})?)?$/";
if (preg_match($pattern, $value, $m)) { return 1; } else { return 0; }
}
How can I fix that $params problems for dates? Phil sorry my English is not good. I didn't understand totally your suggestions. I guess you found reason of problem.
execute(['date_start' => $date_start, 'date_end' => $date_end])
Finally I found reason of problem and I fixed my problem. Actually $params haven't any problem in execute array for dates too. Problem coming from $extra=rtrim($extra, ' and '); I changed to $extra=substr($extra, 0, -5); again and problem fixed.
This is correct sql with substr:
date_start >= :date_start and date_end <= :date_end
rtrim didn't give any error for other parameters but when I used rtrim for date_end sql it happened this way:
date_start >= :date_start and date_end <= :date_e
(nd character removed from date_end in sql)
Thanks your answers and your suggestions.
I hope someone can help me with the best way to structure this code.
I run an oil club as a volunteer and if there are no quotes for a specific oil type, then the page fails due to empty variables being used in queries further down the code.
Ideally I would like to default to $win_supplier_red_id = 0 if the query returns no results, but I'm not sure the best way to catch it and where is the best place in the code.
$sql2a= "Select Quote_id from tbl_quote where (select min(quote_price) as best_red from tbl_quote where fuel_type_id =2 AND timestamp > date_sub( NOW(), INTERVAL 7 DAY ) AND quote_price > 10) = quote_price AND timestamp > date_sub( NOW(), INTERVAL 7 DAY ) Order by timestamp Limit 1";
$stmt2a = $db->prepare($sql2a);
$stmt2a->execute();
$res2a = $stmt2a->fetchObject();
$best_red_quote = $res2a->Quote_id;
$sql2= "SELECT qt.quote_id, ft.fuel_type_id, ft.fuel_name, st.supplier_id, st.company_name as company_name, st.email, qt.supplier_id, qt.timestamp, qt.fuel_type_id, min( qt.quote_price ) AS best_red
FROM tbl_quote qt
INNER JOIN `tbl_suppliers` st ON qt.supplier_id = st.supplier_id
INNER JOIN `tbl_fuel-type` ft ON qt.fuel_type_id = ft.fuel_type_id
WHERE qt.Quote_id = $best_red_quote
Order by timestamp";
$stmt2 = $db->prepare($sql2);
$stmt2->execute();
$res2 = $stmt2->fetchObject();
$best_red = $res2->best_red;
$winning_supplier_red = $res2->company_name;
$win_supplier_red_id = $res2->supplier_id;
$stmt2 = $db->prepare($sql2);
$stmt2->execute();
$res2 = $stmt2->fetchObject();
if (is_object($res2)) {
$best_red = $res2->best_red;
$winning_supplier_red = $res2->company_name;
$win_supplier_red_id = isset($res2->supplier_id) ? $res2->supplier_id : 0;
} else {
$best_red = '';
$winning_supplier_red = '';
$win_supplier_red_id = 0;
}
I'm really stumped here, I've tried using:
sum(case when date_format(from_unixtime(l.date_updated), '%Y-%m-%d') = date_format(now(), '%Y-%m-%d') then 1 else 0 end) AS day0_leads,
in my query, and it did not work as intended, so I ended up using this:
<?php
$total_days = '14';
for ($i = $total_days - 1; $i >= 0; $i--)
{
$day = strtotime('-'.$i.' days');
$day_string = date('n/j', $day);
$leads = mysql_result(mysql_query("select count(*) from `leads` where date_format(from_unixtime(`date_updated`), '%m-%d-%Y') = date_format(from_unixtime($day), '%m-%d-%Y')"), 0);
$assigns = mysql_result(mysql_query("select count(*) from `assigns` where date_format(from_unixtime(`date_assigned`), '%m-%d-%Y') = date_format(from_unixtime($day), '%m-%d-%Y') and `id_dealer` not in (1,2,3)"), 0);
echo "['$day_string', $leads, $assigns]";
if ($i > 0)
echo ',';
}
?>
It is making the page load slow, obviously due to unnecessary queries. What is the proper way of writing this as a single query and outputting the results? Like I said, I've tried a sum with a then else, and it did not product the correct numbers.
Any help would greatly be appreciated.
Heres my solution:
$total_days = '14';
// get leads count array
$sql = mysql_query("select count(*) as `count`, `date_updated`
from `leads`
where date_format(from_unixtime(`date_updated`), '%Y-%m-%d') >= date_format(now() - interval $total_days day, '%Y-%m-%d')
group by date(from_unixtime(`date_updated`));") or die(mysql_error());
$leads_count = array();
while ($row = mysql_fetch_assoc($sql))
$leads_count[date('n/j', $row['date_updated'])] = $row['count'];
// get assigns count array
$sql = mysql_query("select count(*) as `count`, `date_assigned`
from `assigns`
where date_format(from_unixtime(`date_assigned`), '%Y-%m-%d') >= date_format(now() - interval $total_days day, '%Y-%m-%d') and `id_dealer` not in (1,2,3,4)
group by date(from_unixtime(`date_assigned`));") or die(mysql_error());
$assigns_count = array();
while ($row = mysql_fetch_assoc($sql))
$assigns_count[date('n/j', $row['date_assigned'])] = $row['count'];
for ($i = $total_days - 1; $i >= 0; $i--)
{
$day = strtotime('-'.$i.' days');
$day_string = date('n/j', $day);
$leads = ((empty($leads_count[$day_string])) ? '0' : $leads_count[$day_string]);
$assigns = ((empty($assigns_count[$day_string])) ? '0' : $assigns_count[$day_string]);
echo "['$day_string', $leads, $assigns]";
if ($i > 0)
echo ',';
}
On my blog I am have a function that counts how many blog posts have been made month by month and these can be displayed in a sub menu.
It is looped by year, and then another nested loop by month. Each time it runs this query to count how many articles were posted in each month.
This is the query:
Select * from DM_blog blog where blog.id IN
( SELECT entry_id FROM DM_tags_target tagstarget WHERE tagstarget.parent_id IN
( SELECT id FROM DM_tags tags WHERE tags.tag = '".$data['page']['id']."' AND tags.type = 'blog_target' )) AND blog.publish < '".date("Y-m-d H:i:s")."' and blog.status = '0' and YEAR(blog.publish) = '".$year."' and MONTH(blog.publish) = '".$month."'"
Is there a better way to do this? Can I do all of the months at once instead of having to do 12 queries a year? The full code is here:
$this->benchmark->mark('blog_submenu_start');
$dates = $this->blog_model->menu_dates($data);
if($dates['years']){
$i = 0;
foreach($dates['years'] as $year){
if($data['segments']['modal'] == $year['YEAR(blog.publish)']){
$data['date_submenu']['year'][$year['YEAR(blog.publish)']]['class'] = 'selected';
$selected = true;
}else if(date("Y") == $year['YEAR(blog.publish)'] && $selected == false){
$data['date_submenu']['year'][$year['YEAR(blog.publish)']]['class'] = 'selected';
$selected = true;
}
// find the start month
if($dates['first_entry'][0]['YEAR(blog.publish)'] == $year['YEAR(blog.publish)']){
if($dates['first_entry'][0]['MONTH(blog.publish)'] < 2){
$limit_s['start'] = '1';
$limit_s['end'] = 13;
}else{
$limit_s['start'] = $dates['first_entry'][0]['MONTH(blog.publish)'];
$limit_s['end'] = 12;
}
}else{
$limit_s['start'] = 1;
$limit_s['end'] = 13;
}
// run through the months
$this->benchmark->mark('blog_submenu_dates_'.$year['YEAR(blog.publish)'].'_start');
for($x=$limit_s['start'];$x<=$limit_s['end'];$x++){
$this->benchmark->mark('blog_submenu_dates_'.$year['YEAR(blog.publish)'].'_'.$x.'_start');
$this->benchmark->mark('blog_submenu_dates_'.$year['YEAR(blog.publish)'].'_'.$x.'_mysql_start');
$count = $this->blog_model->month_count($year['YEAR(blog.publish)'],$x,$data);
$this->benchmark->mark('blog_submenu_dates_'.$year['YEAR(blog.publish)'].'_'.$x.'_mysql_end');
if($last == false){
$this->benchmark->mark('blog_submenu_dates_'.$year['YEAR(blog.publish)'].'_'.$x.'_selected_start');
if($data['segments']['modal'] == $year['YEAR(blog.publish)'] and $data['segments']['para_one'] == $x){
$data['date_submenu']['year'][$year['YEAR(blog.publish)']]['months'][$x]['class'] = 'selected';
}
$this->benchmark->mark('blog_submenu_dates_'.$year['YEAR(blog.publish)'].'_'.$x.'_selected_end');
$this->benchmark->mark('blog_submenu_dates_'.$year['YEAR(blog.publish)'].'_'.$x.'_assign_start');
$data['date_submenu']['year'][$year['YEAR(blog.publish)']]['months'][$x]['month'] = $x;
$data['date_submenu']['year'][$year['YEAR(blog.publish)']]['months'][$x]['display_full'] = date('F',strtotime($year['YEAR(blog.publish)'].'-'.$x.'-01'));
$data['date_submenu']['year'][$year['YEAR(blog.publish)']]['months'][$x]['display_short'] = date('M',strtotime($year['YEAR(blog.publish)'].'-'.$x.'-01'));
$data['date_submenu']['year'][$year['YEAR(blog.publish)']]['months'][$x]['count'] = $count;
$data['date_submenu']['year'][$year['YEAR(blog.publish)']]['months'][$x]['sef'] = $year['YEAR(blog.publish)'].'/'.$x.'/';
$this->benchmark->mark('blog_submenu_dates_'.$year['YEAR(blog.publish)'].'_'.$x.'_assign_end');
if(date("Y") == $year['YEAR(blog.publish)'] and date("m") == $x || date("n") == $x){
$last = true;
} // end date
$i++;
$this->benchmark->mark('blog_submenu_dates_'.$year['YEAR(blog.publish)'].'_'.$x.'_end');
} // end last false
} // end for
$this->benchmark->mark('blog_submenu_dates_'.$year['YEAR(blog.publish)'].'_end');
}
}
$this->benchmark->mark('blog_submenu_end');
$this->blog_model->month_count - this function is as follows:
function month_count($year,$month,$data=false){
$query = $this->db->query("Select * from DM_blog blog where blog.id IN
( SELECT entry_id FROM DM_tags_target tagstarget WHERE tagstarget.parent_id IN
( SELECT id FROM DM_tags tags WHERE tags.tag = '".$data['page']['id']."' AND tags.type = 'blog_target' )) AND blog.publish < '".date("Y-m-d H:i:s")."' and blog.status = '0' and YEAR(blog.publish) = '".$year."' and MONTH(blog.publish) = '".$month."'");
return $query->num_rows();
}
You can add group by clause to group the results by year and month.
Select * from DM_blog blog where blog.id IN
( SELECT entry_id FROM DM_tags_target tagstarget WHERE tagstarget.parent_id IN
( SELECT id FROM DM_tags tags WHERE tags.tag = '".$data['page']['id']."' AND tags.type = 'blog_target' )) AND blog.publish < '".date("Y-m-d H:i:s")."' and blog.status = '0' GROUP BY YEAR(blog.publish), MONTH(blog.publish)
You could use a GROUP BY expression:
SELECT id, YEAR(blog.publish), MONTH(blog.publish)
FROM DM_blog blog
WHERE blog.id IN
( SELECT entry_id
FROM DM_tags_target tagstarget
WHERE tagstarget.parent_id IN
( SELECT id
FROM DM_tags tags
WHERE tags.tag = '".$data['page']['id']."'
AND tags.type = 'blog_target'
)
)
AND blog.publish < '".date("Y-m-d H:i:s")."'
AND blog.status = '0'
GROUP BY YEAR(blog.publish), MONTH(blog.publish)
It will give you the results grouped by year and month