reducing mysql statements that differ only in ORDER BY field name - php

I'm trying to sort data by different fields ascending and descending. But I have different mysql pdo statements for the 4 fields I have (8 queries total):
$stmt1 = $po->prepare("SELECT * FROM tabname WHERE categ=:categ ORDER BY field1 DESC");
$stmt2 = $po->prepare("SELECT * FROM tabname WHERE categ=:categ ORDER BY field1 ASC");
$stmt3 = $po->prepare("SELECT * FROM tabname WHERE categ=:categ ORDER BY field2 DESC");
$stmt4 = $po->prepare("SELECT * FROM tabname WHERE categ=:categ ORDER BY field3 ASC");
$stmt5 = $po->prepare("SELECT * FROM tabname WHERE categ=:categ ORDER BY field3 DESC");
$stmt6 = $po->prepare("SELECT * FROM tabname WHERE categ=:categ ORDER BY field3 ASC");
$stmt7 = $po->prepare("SELECT * FROM tabname WHERE categ=:categ ORDER BY field4 DESC");
$stmt8 = $po->prepare("SELECT * FROM tabname WHERE categ=:categ ORDER BY field4 ASC");
Based on input, I pick the right statement and bind and execute it.
if ($sortcode == 1){
$stmt1->bindParam(':categ', $categ, PDO::PARAM_STR);
$stmt1->execute();
$fetched = $stmt1->fetchAll(PDO::FETCH_ASSOC);
} else if ($sortcode == 2){
$stmt2->bindParam(':categ', $categ, PDO::PARAM_STR);
$stmt2->execute();
$fetched = $stmt2->fetchAll(PDO::FETCH_ASSOC);
} else if ($sortcode == 3){
$stmt3->bindParam(':categ', $categ, PDO::PARAM_STR);
$stmt3->execute();
$fetched = $stmt3->fetchAll(PDO::FETCH_ASSOC);
}
//repeat the block 5 more times, for a total of 8
This doesn't look right at all. Since the select statements only differ int he name of the field and the desc/asc, is there a better way to get the $sortcode and compact the code that follows?
I guess I could state the question more specifically as: is there a way I could have a single statement/single pdo statement that binds the field name and asc/decs dynamically?

Use associative arrays to hold the prepared statements.
Your input is a column and a sorting method, right? So prepare the queries by:
$columns = array("field1", "field2", "field3", "field4");
$orders = array("asc", "desc");
$queries = array();
foreach($columns as $col) {
$queries[$column] = array();
foreach ($orders as $order) {
$queries[$column][$order] = $po->prepare("SELECT * FROM tabname WHERE categ=:categ ORDER BY $column $order");
}
}
Now, to look up the correct query, you don't need some synthetic code -- you just look it up by column and order directly.
To look up a query, instead of having your users input a number from 1-8, have them input a column and an order. Imagine that the column is in the variable $col and the order is in $ord. Just say $queries[$col][$ord].
If for some reason you have to use the number (why?), then you need a slightly different strategy. In that case, you store the queries by that number.
$columns = array("field1", "field2", "field3", "field4");
$orders = array("asc", "desc");
$queries = array();
$i = 0;
foreach($columns as $col) {
foreach ($orders as $order) {
$i = $i + 1;
$queries[$i] = $po->prepare("SELECT * FROM tabname WHERE categ=:categ ORDER BY $column $order");
}
}
In other words, you ought to be storing queries according to how you plan to look them up.

You can order by column number, which can be parameterized. Using an ORDER BY in this fashion is usually clearer if you specify the columns in the select clause. I'm not sure if ASC/DESC can be parameterized, though...

You can always build the queries dynamically. Store the map between $sortcode and the columns, the SELECT * FROM tabname WHERE categ=:categ part of the query, get the right column name based on $sortcode and add the ORDER BY ... part.

Related

WHERE clause in MySQL for selecting all

I have a script in PHP, which selects data from a Mysql Database using Select & Where clause.
$lrn= "PU2017LN11K";
$stmt = $user_home->runQuery('SELECT * FROM mcq WHERE LRN = :crn ORDER BY LRN,Sr ASC ');
$stmt->bindParam(':crn',$lrn);
$stmt->execute();
Now how to script the code so that it selects from all the LRN, using WHERE LRN = :crn.
I tried:
$lrn= "*";
$stmt = $user_home->runQuery('SELECT * FROM mcq WHERE LRN = :crn ORDER BY LRN,Sr ASC ');
$stmt->bindParam(':crn',$lrn);
$stmt->execute();
But it didn't show any results!
It's easy to skip the WHERE clause if I want to show all results, but
the value of $lrn will be defined by the $_GET method.
The normal way of handling this would be:
$lrn= "*";
$stmt = $user_home->runQuery('SELECT * FROM mcq WHERE LRN = :crn OR :crn = \'*\' ORDER BY LRN, Sr ASC ');
Alternatively, you could use LIKE:
$lrn = "%";
$stmt = $user_home->runQuery('SELECT * FROM mcq WHERE LRN LIKE :crn ORDER BY LRN, Sr ASC ');
You want all rows, don't use a where clause. Alternatively, if you must use a where (something in your code or thinking/design requires it), then you can also say something like "where 1" (like the way phpMyAdmin does things all the time, for what ever reason).
Use this as your where clause:
WHERE LRN = CASE WHEN :crn = '*' THEN LRN else :crn END
That way when the supplied :crn is '*' you will get all records.

How to add results of multiple queries to an array in PDO?

Below is my code
$stmt = $db->prepare("SELECT * FROM inbox ORDER BY `date` DESC ");
$stmt->execute(array());
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
$uniques = count(array_unique($row, SORT_REGULAR));
for ($i=0; $i < $uniques; $i++) {
$inbox_id = $row[$i]["inbox_id"];
$stmt = $db->prepare("SELECT * FROM messages WHERE inbox_id=? AND seen=? ORDER BY `date` DESC");
$stmt->execute(array($inbox_id, 0));
$row_msg = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
return $row_msg;
But this is nor returning all data.It is only returning values got from first iteration only.
you can use:
$row_msg[$i] = $stmt->fetchAll(PDO::FETCH_ASSOC);
Your PDO statement is prepared every round inside the loop, that is not necessary and i hope the inbox_id column is a primary key in the inbox table:
$stmt1 = $db->query("SELECT `inbox_id` FROM inbox ORDER BY `date` DESC ");
$stmt2 = $db->prepare("SELECT * FROM messages WHERE inbox_id=? AND seen=0 ORDER BY `date` DESC");
while($id = $stmt1->fetchColumn(0)) {
$stmt2->execute(array($id));
$row_msg[$id] = $stmt2->fetchAll(PDO::FETCH_ASSOC);
}
return $row_msg;
If you do not want a multidimensional array you can just add the row to the result array by using array_merge() (PHP Manual: array_merge) inside the loop (instead of the line with $row_msg[$id]):
$row_msg = array_merge($row_msg, $stmt2->fetchAll(PDO::FETCH_ASSOC));

SQL Select count of repeated dates

I'm trying to select count of repeated dates and output its numbers
$user_curr_id = $_SESSION['user_id'];
$sql = "SELECT COUNT(datum) FROM table_name WHERE user_ids = $user_curr_id";
I have no idea how to do it.
2014-07-23,2014-07-23,2014-07-23 => 3
2014-07-24,2014-07-24 =>2
2014-07-25 => 1
and get $result = 3,2,1
I assume you're looking after the GROUP BY clause:
$sql = "SELECT datum, COUNT(datum) as cnt
FROM table_name
WHERE user_ids = $user_curr_id
GROUP BY datum
ORDER BY COUNT(datum) DESC;";
if your column datum is of the data type DATE.
Note
As already mentioned you're vulnerable to sql injection. You should use a parameterized prepared statement and bind your input value to this parameter like that:
$sql = "SELECT datum, COUNT(datum) cnt
FROM table_name
WHERE user_ids = ?
GROUP BY datum
ORDER BY COUNT(datum) DESC;";
$result = array();
if ($stmt = $mysqli->prepare($sql)) {
if ($stmt->bind_param('s', $user_curr_id)) {
if($res = $stmt->execute()) {
while ($row = $res->fetch_assoc()) {
$result[] = $row['cnt']; // add the content of field cnt
}
}
}
}
echo implode(',', $result);

php MySQL query not returning anything

I'm not sure exactly how to explain what the query does, however the problem isn't entirely with how it's set up, because it does work, in another instance, when I use it as an array, however it's not working when I use it with mysql_fetch_assoc(), so here is what my original query is(not the one im having trouble with):
SELECT * FROM
(SELECT * FROM comments
WHERE postID='$id' AND state='0'
ORDER BY id DESC LIMIT 3
) t ORDER BY id ASC
what this does is selects the last 3 comments on a post, then orders them in another way (so they show up in the correct order, old to new) Now this is the query for echoing out the array of comments directly.
But now what I want to do, is to just get the first id out of the 3 comments.
here's what I have tried to do (and by the way, this query DOES work, when i replace my previous query to echo out the results in an array, but i need to get just the id for use, i don't want an array):
$previousIDq = mysql_fetch_assoc(mysql_query("
SELECT * FROM
(SELECT * FROM comments
WHERE postID='$id' AND state='0'
ORDER BY id DESC LIMIT 3
) t ORDER BY id ASC LIMIT 1"));
$previousID = $previousIDq['id']; //this doesn't return the id as I want it to.
Your problem may be that there are no matching rows.
Also, I think you could also improve your query to this:
SELECT * FROM comments WHERE postID='$id' AND state='0' ORDER BY id DESC LIMIT 2,1
But as others say, use PDO or MySQLi, and with prepared statements. And don't SELECT * ever.
try a var_dump($previousID) to see what you get
It is probably giving you back an object, and you need to get your id from that object
You script is too condensened for error handling, let alone debugging
$mysql = mysql_connect(...
$query = "
SELECT * FROM
(SELECT * FROM comments
WHERE postID='$id' AND state='0'
ORDER BY id DESC LIMIT 3
) t ORDER BY id ASC LIMIT 1
";
$result = mysql_query($query, $mysql);
if ( !$result ) { // query failed
die('<pre>'.htmlspecialchars(mysql_error($mysql)).'</pre><pre>'.htmlspecialchars($query).'</pre>');
}
$previousIDq = mysql_fetch_assoc($result);
if ( !$previousIDq ) {
die('empty result set');
}
else {
$previousID = $previousIDq['id'];
}
You need to separate your code to be able to debug
$query = "SELECT * FROM
(SELECT * FROM comments
WHERE postID='$id' AND state='0'
ORDER BY id DESC LIMIT 3
) t ORDER BY id ASC LIMIT 1";
$result = mysql_query($query);
echo mysql_error(); //what eror comes out here
while($previousIDq = mysql_fetch_assoc($result))
{
print ($previousIDq['id']);
}
NOTE: mysql_* is depreciated, upgrade to mysqli or PDO
Please stop using mysql_ functions to write new code, it is being deprecated. Use mysqli_ or PDO functions (mysqli below).
Bind your parameters to prevent SQL injection
Always use a column list (don't use SELECT *)
If you're returning > 1 row, use a while loop
mysqli_ solution
$link = mysqli_connect("localhost", "user_name", "password", "stock");
if (mysqli_connect_error()) {
die('Connect Error (' . mysqli_connect_errno() . ') ' . mysqli_connect_error());
}
$stmt = mysqli_prepare($link, "SELECT t.id FROM
(SELECT id FROM comments
WHERE postID = ? AND state = 0
ORDER BY id DESC
LIMIT 3) t
ORDER BY id ASC");
mysqli_bind_param($stmt, 's', $id) or die(mysqli_error($dbh));
$result = mysqli_stmt_execute($stmt) or die(mysqli_error($link));
while($row = mysqli_fetch_assoc($result)) {
echo $row[id];
}
mysqli_close($link);
mysql_ solution
$stmt = "SELECT t.id FROM
(SELECT id FROM comments
WHERE postID = $id AND state = 0
ORDER BY id DESC
LIMIT 3) t
ORDER BY id ASC";
$result = mysql_query($stmt) or die(mysql_error());
$row = mysql_fetch_assoc($result);
while($row = mysql_fetch_assoc($result)) {
echo $row[id];
}

pdo binding asc/desc order dynamically

Let's say I have 2 pdo statements that differ only in order (asc vs. desc)
$stmt1 = $po->prepare("SELECT * FROM tabname WHERE categ=:categ ORDER BY field1 DESC");
$stmt2 = $po->prepare("SELECT * FROM tabname WHERE categ=:categ ORDER BY field1 ASC");
Is there a way I can bind ASC/DESC dynamically so I can have only 1 stmt
$order = "ASC"; //or "DESC"
$stmt = $po->prepare("SELECT * FROM tabname WHERE categ=:categ ORDER BY field1 order=:order");
$stmt->bindParam(':order', $order, PDO::PARAM_STR);
no. parameters are automatically quoted, and ASC/DESC shouldn't be quoted. this is the same reason that table and column names can't be parameters.
What I did was create a variable in $_session called "task_order" and set it to 0 by default. Then, in the sql statement I call a private function/switch statement that determines if ASC or DESC should be added to the sql statement. If it's 0, then it returns "ASC" and sets "task_order" to 1. If it's 1, it does the opposite. So it works like a "toggle" mechanism.
I understand this is an old question/topic, but I stumbled upon it be searching, so maybe someone else will as well. If you have a better idea, please share!
EDIT:found some old code:
$sql = "SELECT * FROM tasks WHERE owner = ? ORDER BY priority " . $this->check_sort_status() . "";
and the method that I call is :
public function check_sort_status() {
switch ($_SESSION["asc"]) {
case 0:
$_SESSION["asc"] = 1;
return "ASC";
case 1:
$_SESSION["asc"] = 0;
return "DESC";
}

Categories