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

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.

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)

Is there a variable for mysql that returns all?

I have a MySQL query using a WHERE clause based on a variable. I am trying to figure out if there is a way to set the variable to something like a wildcard that would return any value in that field?
WHERE c.call_state_id = $state
AND (follow_emp_id = $user_id OR follow_emp_id = 117)
ORDER BY $sort
Instead of limiting the results to 1 specific $user_id, I want to virtually eliminate the AND portion of the query.
I would build the query dynamically, and check for the value before putting it into the WHERE clause.
if (empty($userid)) {
$userid_check = ""
} else {
$userid_check = "AND (follow_emp_id = $user_id OR follow_emp_id = 117)"
}
$sql = "SELECT ...
WHERE c.call_state_id = $state
$userid_check
ORDER BY $sort";
See https://stackoverflow.com/a/28909923/1491895 for a more general approach to building queries dynamically.
If you know a value that will not be used, such as 0 (or -1); you can just check for that:
WHERE c.call_state_id = $state
AND ($user_id = 0 OR follow_emp_id = $user_id OR follow_emp_id = 117)
ORDER BY $sort

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);

Eloquent: Nested query to revert order in limited result

The following function (in my User Model) gives me the correct result for my chat system. Almost... I need to revert the order of the results.
public function getChatConv($cp, $page=1){
$limit = $page * 20;
$user = Authek::curUser();
$res = Chatmessage::where('receipient',$cp)->where('sender',$user->id)
->orWhere('receipient',$user->id)->where('sender',$cp)
->orderBy('created_at','desc')->take($limit)->get();
return $res;
}
It returns an object and I need an object as result. I tried already to convert the result to an array, revert the order and then convert the array back to object. This didn't work.
What I need is a nested query like the following raw SQL query:
SELECT *
FROM (
SELECT *
FROM chatmessages
WHERE (
receipient = '422'
AND sender = '22'
)
OR (
receipient = '22'
AND sender = '422'
)
ORDER BY created_at DESC
LIMIT 0 , 20
)faketable
ORDER BY created_at ASC
There are a few articles with nested queries, but I don't find a similar case and it would be good if someone could do this in Eloquent without the use of Raw queries... It must be possible.
Try this..
use take() and skip(),offset()
get 4 items from offset 3/4th:
Chatmessage::take(4)->offset(3)->get();
Or this (get 10 items from 8rd row):
Chatmessage::take(10)->skip(2)->get();
public function getChatConv($cp, $page=1){
$limit = $page * 20;
$user = Authek::curUser();
$res = Chatmessage::where('receipient',$cp)->where('sender',$user->id)
->orWhere('receipient',$user->id)->where('sender',$cp)
->orderBy('created_at','desc')->take(3)->skip(2)->get();
return $res;
}

Database query for desired results

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;
}