Order by date / price - php

I want to show database records ordered by Datetime or Price when users click on links sort by Datetime or sort by Price. Should I add a condition into the query ?
$sql = "SELECT p.title AS Title, p.date_published) AS Datetime , p.price AS Price, c.name AS Category FROM products p
INNER JOIN categories c
ON c.id = p.category
INNER JOIN brands b
ON b.id = p.brand
.
.
.
";
if (isset($_GET['search'])){
$locations = array();
$getters = array();
$queries = array();
foreach($_GET as $key => $value) {
.
.
.
}
if (!empty($brands)) {
$brd_q = implode(",",$brands);
}
if(!empty($getters)) {
foreach($getters as $key => $value){
${$key} = $value;
switch($key) {
case 'search':
array_push($queries, "(p.title LIKE '%$search%' || p.description LIKE '%$search%' || p.number LIKE '%$search%')");
break;
case 'scategory':
array_push($queries, "p.category = $scategory");
break;
case 'sbrands':
array_push($queries, "p_brd.brand_id IN ($brd_q)");
break;
.
.
.
}
}
}
if(!empty($queries)) {
$sql .= " WHERE ";
$i=1;
foreach($queries as $query) {
if ($i < count($queries)) {
$sql .= $query." AND ";
}else{
$sql .= $query;
}
$i++;
}
}
$sql .= " ORDER BY Datetime DESC";
}

You already have this clause:
$sql .= " ORDER BY Datetime DESC";
All you need, is to add ordering parameter to your query, say $_GET['order'] as integer index(1-based) in your whitelist orders where sign points to order direction.
$orders = ['Datetime', 'Price'];
if (empty($_GET['order'])) $_GET['order'] = -1; // set default order
$index = abs($_GET['order'])-1;
$ord = isset($orders[$index]) ? $orders[$index] : $orders[0];
$direction = $_GET['order'] > 0 ? 'ASC' : 'DESC';
$sql .= " ORDER BY {$ord} {$direction}";

In simple way you can use if else condition for sort query only. According to your condition like this.
$sql = "Rest of the Query..";
if($_GET['order'] == 'price') {
$sql .= " ORDER BY Price DESC";
} else {
$sql .= " ORDER BY Datetime DESC";
}
When user click Sort by Price or Datetime then query will executed as per the condition.
(Or)
You can update query case condition like below example.
ORDER BY
CASE WHEN "price" = $_GET['order'] THEN price END DESC,
CASE WHEN "datetime" = $_GET['order'] THEN datetime END DESC
(or)
ORDER BY
CASE $_GET['order']
WHEN "price" THEN price END DESC,
WHEN "datetime" THEN datetime END DESC
etc...
Whatever you choose booth will be same performance and efficiency.

Related

mysql dynamic queries without clause where

In the following example there is a base query. Other parameters can be dynamically added to complete the query.
However, my base query has no clause WHERE.
What is the best way to deal with it.
If I use in the base query, for example, WHERE 1 = 1, it seems to solve, but I have some doubts that is a correct solution.
$myQuery = "SELECT fr.oranges, fr.aplles, fr.bananas,
FROM fruits fr
LEFT JOIN countrys ct ON fr.id_fruit = ct.id_fruit";
if(!empty($countrys){
$myQuery .= " AND countrys = ? ";
}
if(!empty($sellers){
$myQuery .= " AND seller = ? ";
}
$myQuery .=" GROUP BY fr.id_fruit ORDER BY fr.fruit ASC";
Edited: I fixed a writing gap from $empty to empty.
The WHERE 1=1 is a simplistic hack that works well because it simplifies your code. There is a great post here which explains the performance implications of WHERE 1=1. The general consensus is it will have no effect on performance.
Also, slight note ($empty) is probably not a function you've defined. I think you want empty(). You could write it like this:
$myQuery = "SELECT fr.oranges, fr.aplles, fr.bananas,
FROM fruits fr
LEFT JOIN countrys ct ON fr.id_fruit = ct.id_fruit";
$where = [];
if(!empty($countrys){
$where[] = "countrys = ?";
}
if(!empty($sellers){
$where[] = "seller = ?";
}
if (!empty($where)) {
$myQuery .= " WHERE " . implode(" AND ", $where);
}
$myQuery .= " GROUP BY fr.id_fruit ORDER BY fr.fruit ASC";
You can use an array to control your SQL like this:
$where = [];
if(!$empty($countrys){
$where[] = " countrys = ? ";
}
if(!$empty($sellers){
$where[] = " seller = ? ";
}
if(count($where) > 0) {
$myQuery .= " WHERE ".implode('AND', $where);
}

Join two queries in mysql/php

I need some help with this:
I'm doing a search with several parameters: Category, date(from), date(to)
Here's the code:
if (count($categorias) > 0) {
for ($i = 0; $i < count($categorias); $i++) {
if ($i == 0) {
$query = "WHERE id_subcategoria = " . $categorias[$i] . " ";
} else {
$query .= " OR id_subcategoria = " . $categorias[$i] . " ";
}
}
} else {
$query = "";
}
if (!$sql = $db->sql_query("SELECT * FROM actividades $query ORDER BY id DESC LIMIT " . $pageLimit . "," . PAGE_PER_NO)) {
message_die("DS", TRUE);
}
$total = $db->sql_numrows($sql);
if ($total != 0) {
while ($linha = $db->sql_fetchrow($sql)) {
$SqlEstado = $db->sql_query("SELECT * FROM tarifas WHERE id_actividade = " . $linha['id'] . " AND dia = '$data_min'");
First I get all the products that match the category and then I go to table "tarifas" to check if the product is available to be booked on that day.
So my question is: Can I do it all in one query? selecting the product and in case there's a rule in table "tarifas" fetch that rule.
If it's not clear enough please let me know so I can expose the problem in a different way, i'm really struggling with this.
Yes, you perform a JOIN on the id columns. If you want just those products that are in "tarifas" it will be an INNER JOIN, if you want the products even if they aren't in "tarifas" it will be "actividades LEFT JOIN tarifas ON ...".
Thanks for the help, user3038802!
Here's the code in case it helps anyone with the same problem:
if (!$sql = $db->sql_query("SELECT actividades.nome, actividades.id, actividades.dias, actividades.preco FROM actividades LEFT JOIN tarifas ON actividades.id = tarifas.id_actividade AND tarifas.dia = '$data_min' $query")) {
message_die("DS", TRUE);
}

Cleaner way to write this MySql query

I generate below query through php
$sql .= "Select * from test";
$sub_sql = '';
foreach($value as $n_k => $n_v)
{
switch($n_v)
{
case '1':
$sub_sql .= " OR (id = 1 )";
break;
case '2':
$sub_sql .= " OR (category = 4) ";
break;
case '3':
$sub_sql .= " OR (location = 'U.S.') ";
break;
}
}
if($sub_sql != '')
{
$sql .= " where ( 0 ".$sub_sql." ) ";
}
$sql .= "GROUP BY id ORDER BY stored_date DESC LIMIT 0, 5 ";
But as you'll can see where $subsql is concatenated to $sql, that part looks really messy. Evn though it works fine but it looks really poor. Can anyone pls help me with this?
How about adding the conditions to the sql?
Something like:
Select * from test where a=1
AND
(
($n_v = 1 AND id = 1)
OR
($n_v = 2 AND category = 4)
OR
($n_v = 3 AND location = 'U.S.')
)
Is this a simplified example? Because I see no reason not to simplify it like this:
$sql .= "Select * from test where a=1";
foreach($value as $n_k => $n_v)
{
switch($n_v)
{
case '1':
$sql .= " AND (id = 1 )";
break;
case '2':
$sql .= " AND (category = 4)";
break;
case '3':
$sql .= " AND (location = 'U.S.')";
break;
}
}
$sql .= " ORDER BY stored_date DESC LIMIT 0, 5 ";
Note, that I removed the GROUP BY from your statement. You shouldn't use it, if you don't use aggregate functions. Group by collapses group of rows and displays random rows from each group. If you have a need to remove duplicate rows, consider to use the DISTINCT keyword in the SELECT clause.
UPDATE:
Your new version can be shortened like this:
$sql .= "Select * from test";
foreach($value as $n_k => $n_v)
{
switch($n_v)
{
case '1':
$sql .= " where (id = 1 ) ";
break;
case '2':
$sql .= " where (category = 4) ";
break;
case '3':
$sql .= " where (location = 'U.S.') ";
break;
}
}
$sql .= " GROUP BY id ORDER BY stored_date DESC LIMIT 0, 5 ";
Basically it comes down to this. If you have to add multiple AND conditions, you always add a WHERE 1=1 (if you have no other fix (non-dynamic) conditions) to the query. It doesn't change anything, the optimizer will throw it away again. But you can easily append as many conditions as you want. That's what I did in my original answer before your edit. Note though, that you can not just append OR whatever = true when you are mixing AND and OR conditions. You will have to use parantheses to not get a false resultset. This is where your (0 OR ...) thing comes from. Since you only have 1 condition, there's no need at all for all I said right now. Just add it as WHERE whatever = true and that's it.

Merge these 2 MySQL query into one

I explain you the problem. I have two tables : "listing" who contain all my lists with different informations and particulary one : a city ID. I have a second table "city" who contain the complete list of all cities of my countries with many informations : latitude, longitude, city name, etc.
I want to show all listing ordered by distance between actual point (geolocalised, I have lat/lng) and cities in the listing table.
Actualy, I do this with 2 MySQL queries, and it's work :
$formula = "(6366*acos(cos(radians($latitude))*cos(radians(`latitude`))*cos(radians(`longitude`) -radians($longitude))+sin(radians($latitude))*sin(radians(`latitude`))))";
$q = "SELECT *,$formula AS dist FROM ".$this->db_city." WHERE $formula<='$distance' ORDER by dist ASC";
$q = $this->db->query($q);
$resultSQL = $q->result_array();
And this one :
if ($localization != ''){
if ($whereClause == ''){
//$whereClause .= ' WHERE address_city LIKE "'.$localization.'"';
$loc = '';
foreach ($localization as $key => $value) {
if ($loc == ''){
$loc .= '"'.$value.'"';
}else{
$loc .= ', "'.$value.'"';
}
}
$whereClause .= ' WHERE address_city IN ('.$loc.')';
}else{
//$whereClause .= ' && address_city LIKE "'.$localization.'"';
$loc = '';
foreach ($localization as $key => $value) {
if ($loc == ''){
$loc .= '"'.$value.'"';
}else{
$loc .= ', "'.$value.'"';
}
}
$whereClause .= ' && address_city IN ('.$loc.')';
}
}
$q = "SELECT * FROM ".$this->db_listing.$whereClause." ORDER BY created_stamp DESC";
It's work, but the problem is I don't have the "dist" parameter accessible in this second query, so I can't order by dist. The solution for that is to merge the first big query (the one with the formula) with the second.
I tried with LEFT JOIN, but it didn't work. Can you help me please ? Thanks !
Try this, it will going to return you all the cities those with in the radius of $distance from provided longitude and latitudes.
"SELECT *,(((acos(sin(($latitude*pi()/180)) * sin((`latitude`*pi()/180))+cos(($latitude*pi()/180)) * cos((`Latitude`*pi()/180)) * cos((($longitude-
`longitude`)*pi()/180))))*180/pi())*60*1.1515) AS distance FROM location WHERE (((acos(sin(($latitude*pi()/180)) * sin((`latitude`*pi()/180))+cos(($latitude*pi()/180)) * cos((`Latitude`*pi()/180)) * cos((($longitude-
`longitude`)*pi()/180))))*180/pi())*60*1.1515) <= $distance"
Thanks.

PHP Filtering search results by category with count

I'm trying to have an advanced sidebar that filters the results when someone uses the search bar. I have the regular search working and also made it display the count of items in a category on another sidebar (i.e. price range, brand, condition) but now i want the user to click a category and narrow down the results like what you see on newegg.com.
This is my search query:
function build_query($product_search) {
$search_query = "SELECT * FROM tbl_product p INNER JOIN tbl_brand b ON p.br_id = b.br_id";
$clean_search = str_replace(',', ' ', $product_search);
$search_word = explode(' ', $clean_search);
$final_search_words = array();
if (count($search_word) > 0) {
foreach ($search_word as $word) {
if (!empty($word)) {
$final_search_words[] = $word;
}
}
}
$where_list = array();
if (count($final_search_words) > 0) {
foreach($final_search_words as $word) {
$where_list[] = "p.pd_name LIKE '%$word%'";
}
}
$where_clause = implode(' OR ', $where_list);
if(!empty($where_clause)) {
$search_query .= " WHERE $where_clause";
}
Same thing for my sidebar that counts the categories:
function narrow_query($product_search) {
$search_query = "SELECT p.pd_name, p.pd_con, b.br_name AS name, c.cat_name AS cat,
COUNT(p.pd_con) AS con, COUNT(b.br_name) AS brand, COUNT(c.cat_name) AS cat_c,
COUNT(CASE WHEN p.pd_price >= '00.01' AND p.pd_price <= '99.99' THEN 1 END) AS cnt1,
COUNT(CASE WHEN p.pd_price >= '100.00' AND p.pd_price <= '199.99' THEN 1 END) AS cnt2,
COUNT(CASE WHEN p.pd_price >= '200.00' AND p.pd_price <= '299.99' THEN 1 END) AS cnt3,
And so on and so on...
FROM tbl_product p JOIN tbl_brand b ON b.br_id = p.br_id
JOIN tbl_category c ON c.cat_id = p.cat_id";
$clean_search = str_repl...
(Same word filter code as above)
Now i had something going that kinda worked by grabbing the $_GET:
"<a href=\"" . $_SERVER['PHP_SELF'] . "?productsearch=" . $product_search . "&narrow=" . $row['cat'] . "$link=1\">"
and using case in my functions:
function any_query($product_search, $link, $narrow) {
previous info from above...
if(!empty($link)) {
switch($link)
case 1:
$search_query .= " AND b.br_name = '$narrow'";
break;
default:
}
}
but it's not doing the job and I'm just lost at this point.
Is there a better way or a different method to achieve this? It doesn't have to use case or $_GET.
Thank you for your time.
The problem is you need to add a parentheses around your OR statements. I would do this:
$search_query .= ' WHERE 1';
if (!empty($where_clause)) {
$search_query .= " AND ($where_clause)";
}
if (!empty($narrow)) {
$search_query .= " AND $narrow";
}

Categories