Database query for desired results - mysql

I am using some filters to display the products. Filters like colors, price and stuff.
Link : http://www.applechain.com/products/iPod.php
I use this query
$sql = "Select *
from tbl_product
where device='iPhone'
and (color='$c1'
or color='$c2'
or color='$c3'
or color='$c4'
or color='$c5'
or color='$c6'
or color='$c7'
or color='$c8'
or color='$c9'
or color='$c10'
) and (storage='$cp1'
or storage='$cp2'
or storage='$cp3'
or storage='$cp4'
or storage='$cp5'
) and (f_unlock='$factory')
and (warranty='$warranty')
and (price >= '$price1'
and price <= '$price2'
)
order by product_id desc";
I am using the AND condition for main parameters. Now how do I display the result if my only two parameters gets satisfied. How to achieve that if color and storage parameters gets satisfied,it shows result based on them only irrespective of whether the others are selected or not.

Simple. Remove the other conditions from the query.
BTW, you can rewrite that query this way...
SELECT *
FROM tbl_product
WHERE device = 'iPhone'
AND color IN ('$c1','$c2','$c3','$c4','$c5','$c6','$c7','$c8','$c9','$c10')
AND storage IN ('$cp1','$cp2','$cp3','$cp4','$cp5')
AND f_unlock = '$factory'
AND warranty = '$warranty'
AND price BETWEEN '$price1' and '$price2'
ORDER
BY product_id DESC;

You could build your query based on what is set, normally an ORM tool would do this for you.
As it's probably too late to start using a PHP framework, you should do something like:
if(isset($colours))
{
$sub_query = concatenateOrClauses($colours, 'color');
$main_query .= $sub_query;
}
private function concatenateOrClauses($values, $name)
{
$query_string = "";
for($i = 0; $i < sizeof($values); $i++)
{
if($i == 0)
{
$query_string = $name."=".$values[$i];
}
else
{
$query_string = $query_string." OR ".$name."=".$values[$i];
}
}
return $query_string;
}

Related

MySQL optional filters for search query

I am working on a query that has an optional filter, so lets assume the table name is products and the filter is the id (primary key)
If the filter is not present I would do something like this:
SELECT * FROM products;
If the filter is present I would need to do something like this:
SELECT * FROM products WHERE id = ?;
I have found some potential solutions that can mix the 2 in sql rather than doing conditions in the back-end code itself
SELECT * FROM products WHERE id = IF(? = '', id, ?);
OR
SELECT * FROM products WHERE IF(? = '',1, id = ?);
I was just wondering which one would be faster (In the case of multiple filters or a very big table) Or is there a better solution to handle this kind of situation?
A better approach is to construct the WHERE clause from the parameters available. This allows the Optimizer to do a much better job.
$wheres = array();
// Add on each filter that the user specified:
if (! empty($col)) { $s = $db->db_res->real_escape_string($col);
$wheres[] = "collection = '$s'"; }
if (! empty($theme)) { $s = $db->db_res->real_escape_string($theme);
$wheres[] = "theme = '$s'"; }
if (! empty($city)) { $s = $db->db_res->real_escape_string($city);
$wheres[] = "city = '$s'"; }
if (! empty($tripday)) { $s = $db->db_res->real_escape_string($tripday);
$wheres[] = "tripday = '$s'"; }
// Prefix with WHERE (unless nothing specified):
$where = empty($wheres) ? '' :
'WHERE ' . implode(' AND ', $wheres);
// Use the WHERE clause in the query:
$sql = "SELECT ...
$where
...";
Simplest approach is OR:
SELECT *
FROM products
WHERE (? IS NULL OR id = ?);
Please note that as you will add more and more conditions with AND, generated plan will be at least poor. There is no fit-them-all solution. If possible you should build your query using conditional logic.
More info: The “Kitchen Sink” Procedure (SQL Server - but idea is the same)

Selecting next related row in MySQL

I have a spatial dataset in MySQL 5.7 where I have the columns: id, deviceid, latlng_point, time. latlng_point is a geospatial Point.
What I'm trying to achieve is calculating distance from points. I'm unsure on how to approach this.
SELECT
ST_DISTANCE_SPHERE(latlng_point, i want the next latlng_point here) AS distance
FROM points
WHERE deviceid = 1
ORDER BY time DESC;
In PHP I would do something like this:
<?php
$conn = new mysqli($host,$user,$pass,$db);
$query = "SELECT latlng_point FROM points WHERE deviceid = 1...";
$latlng_array = array();
if ($result = $conn->query($query)) {
while ($row = $result->fetch_assoc()) {
$latlng_array[] = $row;
}
}
$distance = 0;
for ($i = 0; $i < count($latlng_array) - 1; $i++) {
$pt1 = $latlng_array[$i]['latlng_point'];
$pt2 = $latlng_array[$i+1]['latlng_point'];
$distance += haversine_function($pt1,$pt2);
}
echo "Distance: {$distance}";
?>
I'm trying to achieve something similar purely in MySQL.
Try this one:
SELECT SUM(ST_DISTANCE_SPHERE(p1.latlng_point, p2.latlng_point)) AS total_distance
FROM points p1
JOIN points p2 ON p2.id = (
SELECT p.id
FROM points p
WHERE p.deviceid = p1.deviceid
AND p.time > p1.time
ORDER BY p.time ASC
LIMIT 1
)
WHERE p1.deviceid = 1
The (correlated) subquery should return the id of the next point (sorted by time).
I can't tell you if it is really efficient or if it even works at all (can't test it).
However you should have an index on (deviceid, time) - Assuming that id is the primary key.

Implementing mysql intersection

I'm trying to do a filter search functionality in codeigniter. I have a table named products and my system has the functionality to filter these products by category and by date. I have a mysql code in mind which looks something like this:
SELECT * from products WHERE product_category='Cloth'
INTERSECT
SELECT * from products WHERE ('insert date logic here')
So it should return records (via id) from the same table named products. However, there's no INTERSECT in mysql so I don't know how to do this. Any help would be greatly appreciated!
This is my code just for the part of the product category
$this->db->limit($limit,$start);
$query = $this->db->query('SELECT * from product_advertised WHERE quantity > 0 AND prodcatid='.$prodcats[0].' LIMIT '.$start.','.$limit);
if(sizeof($prodcats > 1)) {
$query_str = "SELECT * FROM product_advertised WHERE quantity>0 AND ";
$str="";
for($i = 0;$i < sizeof($prodcats);$i++) {
if($i != sizeof($prodcats)-1) {
$str = $str. "prodcatid=".$prodcats[$i]." OR ";
}
else {
$str = $str. "prodcatid=".$prodcats[$i]." LIMIT ".$start.",".$limit;
}
}
$query_str .= $str;
$query = $this->db->query($query_str);
}
if($query->num_rows() > 0) {
foreach($query->result() as $row) {
$data[] = $row;
}
return $data;
}
return false;
Why wouldn't you just use and?
SELECT *
from products
WHERE product_category = 'Cloth' AND
('insert date logic here');

Mysql PDO (Getting total from all colums) [duplicate]

I'm new to php and I've searched for the past hour and read all the documentation I could find and nothing is helping. I have a table that has a bunch of rows of data. I'm trying to pick one column from the whole table and add them all together. Here is what I got. All this tells me is how many rows there are that match my query, not the total sum of column I want. Any help is appreciated.
$res1 = $db->prepare('SELECT sum(distance) FROM trip_logs WHERE user_id = '. $user_id .' AND status = "2"');
$res1->execute();
$sum_miles = 0;
while($row1 = $res1->fetch(PDO::FETCH_ASSOC)) {
$sum_miles += $row1['distance'];
}
echo $sum_miles;
You're only returning one row in this instance. Modify your summed column to have an alias:
SELECT SUM(distance) AS totDistance FROM trip_logs ....
Now you can can fetch the row -
$row = $res1->fetch(PDO::FETCH_ASSOC);
echo $row['totDistance'];
No need to loop.
You can use SUM() without explicitely grouping your rows because if you use a group function in a statement containing no GROUP BY clause, it is equivalent to grouping on all rows.
If however you want to use the SUM() function for something slightly more complicated you have to group your rows so that the sum can operate on what you want.
If you want to get multiple sums in a single statement, for example to get the distance for all users at once, you need to group the rows explicitely:
$res1 = $db->prepare("
SELECT
SUM(distance) AS distance,
user_id
FROM trip_logs WHERE status = '2'
GROUP BY user_id
");
$res1->execute();
while ($row = $res1->fetch(PDO::FETCH_ASSOC))
{
echo "user $row[user_id] has runned $row[distance] km.\n";
}
This will return the sum of distances by user, not for all users at once.
Try this if you are using a Class :
class Sample_class{
private $db;
public function __construct($database) {
$this->db = $database;
}
public function GetDistant($user_id,$status) {
$query = $this->db->prepare("SELECT sum(distance) FROM trip_logs WHERE user_id =? AND status =?");
$query->bindValue(1, $user_id);
$query->bindValue(2, $status);
try{ $query->execute();
$rows = $query->fetch();
return $rows[0];
} catch (PDOException $e){die($e->getMessage());}
}
}
$dist = new Sample_class($db);
$user_id = 10;
$status = 2;
echo $dist->GetDistant($user_id,$status);

Mysql: Possible errors in queries "WHERE...IN" , "ORDER BY..." and "LIMIT"

As you have observed I have put an ambiguous title for this question, as simply I do not realize (by lack of deep knowledge) if this is the true problem or not.
Let's start then with a short description:
Here is where I select my products:
$select_prod = "SELECT * FROM product WHERE product_id IN ($some_array)";
After that here is where I define the pagination stuff:
$query_page = mysql_query($select_prod);
$product_total = mysql_num_rows($query_page);
$page_rows = 4;
$last = ceil($product_total/$page_rows);
if ($page < 1) {
$page = 1;
} elseif ($page > $last) {
$page = $last;
}
$limit = 'limit ' .($page - 1) * $page_rows .',' .$page_rows;
And where I prepare the render
$page_query = mysql_query($select_prod . $limit);
$results = array();
while ($array_filter = mysql_fetch_array($page_query)) {
$results[] = $array_filter;
}
Until this point everything is flowing easily, and I get my products listed as I wanted, BUT in random ORDER.
I have tried to include "ORDER BY price ASC" at the end of the first query like this:
$select_prod = "SELECT * FROM product WHERE product_id IN ($some_array) ORDER BY price ASC";
but for a strange reason fails to list the products with the error:
Warning: mysql_fetch_array() expects parameter 1 to be resource, boolean given...
I've already had several hours in trying to find where could be the problem, and this forum seems to be the final try for me, after that I would let them order as they want to.
You really need to print out your full query directly before execution. Try this instead:
$select_prod = "SELECT * FROM product WHERE product_id IN ($some_array) ORDER BY price ASC ";
Or, change the limit line to have a space before the limit.
What I believe the problem is the the lack of space before limit. The snippet in ()limit 100 is valid SQL. The snippet in () order by price asclimit 100 is not valid SQL.