PDO compare 2 fields - php

probably a simple case. Basically I have a Database with 5 fields, which are showing Orders and their progress:
idRefs, Quantity, refsDone, refsRunning, refsUntouched
Now I want to select all those Rows, where Quantity = refsDone (this means the order is done). I am wondering how I can do this using PDO ? This is what I've tried:
$filter = $_POST['filter'];
$limiter = $_POST['limiter'];
$refid = $_POST['refid'];
if($refid == "") $refid="%";
case("finished"):
$sql = "SELECT * FROM Orders WHERE REFID LIKE :refid AND quantity=refsDone ORDER BY dateAdded DESC LIMIT :min,:max";
$ps_orders = $db->prepare($sql);
$ps_orders->bindParam(':refid', $refid, PDO::PARAM_STR);
$ps_orders->bindParam(':min', $min, PDO::PARAM_INT);
$ps_orders->bindParam(':max', $limiter, PDO::PARAM_INT);
$ps_orders->execute();
$rowCount = $ps_orders->fetchColumn();
$data = $ps_orders->fetchAll();
break;
Cant say whats happening wrong, but I am kinda sure it is the part "AND quantity=refsDone" which causes it.

I tested your query in this SQLFiddle: http://sqlfiddle.com/#!2/32ae40/8
The query returns an empty set in several cases:
if $refid is null
if $refid is empty string ('')
if $min is 1 or more
if $limiter is 0
I think you should make sure your variables are values you expect them to be, because if they aren't the query is likely to return an empty set.
You can troubleshoot parameterized queries yourself by enabling the MySQL general query log, which logs both the prepared query and the query with parameter values included.

Related

cannot use prepared statements [duplicate]

I'm having problems using params in the ORDER BY section of my SQL. It doesn't issue any warnings, but prints out nothing.
$order = 'columnName';
$direction = 'ASC';
$stmt = $db->prepare("SELECT field from table WHERE column = :my_param ORDER BY :order :direction");
$stmt->bindParam(':my_param', $is_live, PDO::PARAM_STR);
$stmt->bindParam(':order', $order, PDO::PARAM_STR);
$stmt->bindParam(':direction', $direction, PDO::PARAM_STR);
$stmt->execute();
The :my_param works, but not :order or :direction. Is it not being internally escaped correctly? Am I stuck inserting it directly in the SQL? Like so:
$order = 'columnName';
$direction = 'ASC';
$stmt = $db->prepare("SELECT * from table WHERE column = :my_param ORDER BY $order $direction");
Is there a PDO::PARAM_COLUMN_NAME constant or some equivalent?
Thanks!
Yes, you're stuck inserting it directly in the SQL. With some precautions, of course. Every operator/identifier must be hardcoded in your script, like this:
$orders=array("name","price","qty");
$key=array_search($_GET['sort'],$orders);
$order=$orders[$key];
$query="SELECT * from table WHERE is_live = :is_live ORDER BY $order";
Same for the direction.
I wrote a whitelisting helper function to be used in such cases, it greatly reduces the amount of code that needs to be written:
$order = white_list($order, ["name","price","qty"], "Invalid field name");
$direction = white_list($direction, ["ASC","DESC"], "Invalid ORDER BY direction");
$sql = "SELECT field from table WHERE column = ? ORDER BY $order $direction";
$stmt = $db->prepare($sql);
$stmt->execute([$is_live]);
The idea here is to check the value and raise an error in case it is not correct.
I don't think you can :
Use placeholders in an order by clause
Bind column names : you can only bind values -- or variables, and have their value injected in the prepared statement.
It's possible use prepared statements in ORDER BY clause, unfortunately you need pass the order of column insted of the name and is required set PDO_PARAM_INT with type.
In MySQL you can get the order of columns with this query:
SELECT column_name, ordinal_position FROM information_schema.columns
WHERE table_name = 'table' and table_schema = 'database'
PHP code:
$order = 2;
$stmt = $db->prepare("SELECT field from table WHERE column = :param ORDER BY :order DESC");
$stmt->bindParam(':param', $is_live, PDO::PARAM_STR);
$stmt->bindParam(':order', $order, PDO::PARAM_INT);
$stmt->execute();
I don't think you can get ASC/DESC as part of the prepared statement, but the column you can if you list them all in the sql query like so:
// Validate between 2 possible values:
$sortDir = isset($_GET['sortDir']) && $_GET['sortDir'] === 'ASC' ? 'ASC' : 'DESC';
$sql = "
...
order
by
case :orderByCol
when 'email' then email
when 'age' then age
else surname
end
$sortDir
";
$stmt = $db->prepare($sql);
$stmt->bindParam(':orderByCol', $someColumn);
$stmt->execute();
Since ASC/DESC is only two possible values, you can easily validate and select between them as hardcoded values using php code.
You could also make use of the ELT(FIELD(,,,,,),,,,,) functions for this, but then ordering will always be done as a string, even if the column is a numeric data type that should be sorted using numeric semantics / collation.
Unfortunely I guess you could not make it with prepared statements.
It would make it no cacheable since different columns may have values that could be sorted with special sorting strategies.
Create query by using standard escapes and execute it directly.
It is possible . You can use number instead of field name in the 'order by' clause. This is a number starting from 1 and is in the order of field names in the query. And you can concatenate a string in for ASC or DESC. For example
"Select col1,col2,col3 from tab1 order by ? " + strDesc + " limit 10,5".
strDesc=" ASC" / " DESC".
If I'm not entirely mistaken, Pascal is right.
The only binding possible in PDO is the binding of values, as you did with the ':my_param' parameter.
However, there's no harm done in:
$stmt = $db->prepare("SELECT field from table WHERE column = :my_param ORDER BY ".$order ." ".$direction);
$stmt->bindParam(':my_param', $is_live, PDO::PARAM_STR);
$stmt->execute();
The only thing to take notice of would be the correct escaping of $order and $direction, but since you set them manually and didn't set them via user input, I think you're all set.

How To Rank Rows in Descending Order Based on Chosen Column Values And Get Presented Only Qualified Rows?

Imagine my Mysql Tbl looks like this:
Item|Score
cam|2
car|5
window|1
glass|4
Now, I want php mysqli to present rows who's values (in the score column) are greater than "1". From our example, I want it presented like this:
Item|Score
car|5
glass|4
cam|2
NOTE: The row "window|1" has not been presented since it's value in the score column is not greater than "1".
Question 1: Is there a simple way to achieve my purpose without any Groupings done on the Mysql query ? If so, how ?
What would be the Mysql query and what would the php code (Prepared Statements) be when using the Mysqli extension ?
I do not think the following are accurate. Hence, I need your professional inputs.
1st ATTEMPT:
$score = >1;
$query = "SELECT item, score FROM tbl WHERE score = ? ORDER by score DESC";
$stmt = mysqli_prepare($conn,$query);
mysqli_stmt_bind_param($stmt,'i',$score);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_bind_result($stmt,$item, $db_score);
mysqli_stmt_fetch($stmt);
mysqli_stmt_free_result($stmt);
2nd ATTEMPT:
$score = >1;
$query = "SELECT item, score FROM tbl WHERE score > ? ORDER by score DESC";
$stmt = mysqli_prepare($conn,$query);
mysqli_stmt_bind_param($stmt,'i',$score);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_bind_result($stmt,$item, $db_score);
mysqli_stmt_fetch($stmt);
mysqli_stmt_free_result($stmt);

Check if PDO prepare(), execute() returns at least one row

Before moving to PDO, I used
$result = mysqli_query ($conn, 'SELECT * FROM mytable WHERE id = 54');
if (mysqli_num_rows($result) >= 1) { ... }
to check if the query returns at least one result.
Now with PDO, I've seen in many SO questions (like get number of rows with pdo) that there is no direct function in PDO to check the number of rows of a query (there are warnings about the use of$result->rowCount();), but rather solutions like doing an extra query:
SELECT count(*) FROM mytable WHERE id = 54
But this is maybe too much for what I want : in fact, I don't need the exact number of rows, but just if there is at least one.
How to check if a prepared statement query returns at least one row ?
$stmt = $db->prepare('SELECT * FROM mytable WHERE id = 54');
$stmt.execute();
... // HOW TO CHECK HERE?
$stmt = $db->prepare('SELECT * FROM mytable WHERE id = 54');
$stmt.execute();
... // HOW TO CHECK HERE?
It's so simple, you're almost there already.
$stmt = $db->prepare('SELECT * FROM mytable WHERE id = 54');
$stmt.execute();
$result = $stmt->fetchAll(); // Even fetch() will do
if(count($result)>0)
{
// at least 1 row
}
And if you just want Yes/No answer then you should also add a LIMIT 1 to your query so mysql doesn't waste trying to look for more rows.

PDO bindParam not working as expected

This has been bugging for a long time, and I still can't figure out what i'm doing wrong.
In the code I want to select a few users with a comma separated string. The string will always be legit and valid.
In the first example, the one I would like to use, uses bindParam to assign the value of $postId to the SQL query. I have been using bindParam() for lots of other calls, but in this specific case, it fails.
$postId = "1,2,3";
$stm = $this->db->prepare('SELECT * FROM posts WHERE find_in_set(userId, "?") ORDER BY id DESC');
$stm->bindParam(1, $postId, PDO::PARAM_STR);
$stm->setFetchMode(PDO::FETCH_ASSOC);
$stm->execute();
$results = $stm->fetchAll();
return print_r($results,true);
This code returns:
array (
)
In this other code which I really wouldn't like to use, I just pass the value of $postId right into the sql query.
$stm = $this->db->prepare('SELECT * FROM posts WHERE find_in_set(userId, "'.$postId.'") ORDER BY id DESC');
$stm->setFetchMode(PDO::FETCH_ASSOC);
$stm->execute();
$results = $stm->fetchAll();
return print_r($results,true);
This code returns all the rows it is supposed to retrieve.
My question is; What is the specific problem and how can I avoid doing this again?
You shouldn't have quotes around the placeholder in yout query:
$stm = $this->db->prepare('SELECT * FROM posts WHERE find_in_set(userId, ?) ORDER BY id DESC');
See additional docs here.
Although it's not directly related to the question, it's also a handy habit to get into to use named params. When you have only one param to pass, it's not too bad, but when you start getting five or so question marks in the query, it's MUCH easier to actually read if you used named params:
SELECT * FROM posts WHERE find_in_set(userId, :someID) ORDER BY id DESC
Then you bind them as named params in your code:
$sth->bindParam(':someID', $postId, PDO::PARAM_STR);
You don't need to add the double quotes "?" when referencing the value
'SELECT * FROM posts WHERE find_in_set(userId, "?") ORDER BY id DESC'
Should be
'SELECT * FROM posts WHERE find_in_set(userId, ?) ORDER BY id DESC'

PHP SQLSRV Sorting with Parameter of Prepared Statement

I can't figure out why sorting will work as long as I'm not using $sort as a passed in parameter. Example below will work for sorting:
$sort = "quantity desc";
$sql = " with items as (
SELECT i.[item_id]
,i.[name]
,i.[value]
,i.[quantity]
,i.[available]
,isnull(r.awarded, 0) as awarded
, ROW_NUMBER() OVER(
ORDER BY $sort
) rowNumber
FROM [Intranet].[dbo].[Goodwell_Item] i
LEFT JOIN (
SELECT r.item_id
, COUNT(1) awarded
from [Intranet].[dbo].[Goodwell_Reward] r
group by r.item_id
) as r
ON i.item_id = r.item_id
)
SELECT *
FROM items
WHERE rowNumber BETWEEN (?) and (?)
and ( (?) = '' OR (available = (?)))
";
$params = array( $pagify['startFrom'], $end, $available, $available );
$stmt = sqlsrv_query( $conn, $sql, $params );
However if I change the line with ORDER BY to:
ORDER BY (?)
and add it to my $params like so:
$params = array($sort, $pagify['startFrom'], $end, $available, $available );
then the sort for some reason is being ignored.
Please tell me how to get the sort working in a way that doesn't allow SQL injection.
I am dealing with this exact issue right now, and cannot find anything online to help.
I have tried:
$query = "SELECT * FROM {$this->view} WHERE SeriesID = ? ORDER BY ? ";
$result = $conn->getData($query, array($seriesID,$sortBy));
and
$query = "SELECT * FROM {$this->view} WHERE SeriesID = ? ORDER BY ? ?";
$result = $conn->getData($query, array($seriesID,$sortBy,$sortOrder));
In both cases, I get no error, and no results.
I think the only way to solve this safely is to use a switch statement before the query to manually validate the acceptable values. However, unless you're only ever dealing with one table, you can't know what the possible values are for the SortBy column.
However, if you just go with the assumption that the values at this point have already been cleaned, you can go with the non-parameterized version like this:
$query = "SELECT * FROM {$this->view} WHERE SeriesID = ? ORDER BY " . $sortBy . " " . $sortOrder;
$result = $conn->getData($query, array($seriesID));
What I plan to do is make sure to validate sortBy and sortOrder before I pass them to the method that contains this code. By doing it this way, each place I call the code becomes responsible for validating the data before sending it. The calling code would know the valid possible values for the table (or view in this case) that it is calling. (I'm the author of both pieces of code in this case, so I know it's safe.)
So, in short, just make sure that the values at this point in the code are already cleaned and safe, and push that responsibility up one level the code that calls this code.

Categories