In essence, I have a value that I have to call a couple times in my SQL query. Thus, is it possible to reuse the same named placeholder in the statement e.g.
SELECT :Param FROM Table WHERE Column = :Param, then simply bindValue(":Param"), and have the value be there for both :Params?
PDO::prepare states that "you cannot use a named parameter marker of the same name twice in a prepared statement", so I guess that's a no then.
You can if you set PDO::ATTR_EMULATE_PREPARES = true.
E.g. $connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);.
If you're using Laravel you can set this in an options array in config/database.php. e.g. PDO::ATTR_EMULATE_PREPARES => true
Apart from reuse, the main issue here is that you are trying to dynamically change col names.
This answer posted by an anonymous user on http://php.net/manual/en/pdo.prepare.php :
To those wondering why adding quotes to around a placeholder is wrong,
and why you can't use placeholders for table or column names:
There is a common misconception about how the placeholders in prepared
statements work: they are not simply substituted in as (escaped)
strings, and the resulting SQL executed. Instead, a DBMS asked to
"prepare" a statement comes up with a complete query plan for how it
would execute that query, including which tables and indexes it would
use, which will be the same regardless of how you fill in the
placeholders.
The plan for "SELECT name FROM my_table WHERE id = :value" will be the
same whatever you substitute for ":value", but the seemingly similar
"SELECT name FROM :table WHERE id = :value" cannot be planned, because
the DBMS has no idea what table you're actually going to select from.
Even when using "emulated prepares", PDO cannot let you use
placeholders anywhere, because it would have to work out what you
meant: does "Select :foo From some_table" mean ":foo" is going to be a
column reference, or a literal string?
When your query is using a dynamic column reference, you should be explicitly white-listing the columns you know to exist on the table, e.g. using a switch statement with an exception thrown in the default: clause.
Many queries like yours can be rewritten to use only one placeholder.
SELECT :Param FROM Table WHERE Column = :Param
would be the same as
SELECT Column FROM Table WHERE Column = :Param
But sometimes it's not that simple. For example:
SELECT *
FROM my_table
WHERE first_name LIKE :Param
OR last_name LIKE :Param
OR biography LIKE :Param
In such case you could reuse the parameter value storing it in a cross joined derived table (subquery in FROM clause):
SELECT t.*
FROM my_table t
CROSS JOIN (SELECT :Param as Param) AS x
WHERE first_name LIKE x.Param
OR last_name LIKE x.Param
OR biography LIKE x.Param
There's a workaround:
function search($criteria) {
$sql = "SELECT * FROM my_table
WHERE column_1 like CONCAT('%', :criteria1, '%')
OR column_2 like CONCAT('%', :criteria2, '%')
OR column_3 like CONCAT('%', :criteria3, '%')
";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':criteria1', $criteria);
$stmt->bindParam(':criteria2', $criteria);
$stmt->bindParam(':criteria3', $criteria);
$stmt->execute();
return($stmt->fetchAll(PDO::FETCH_ASSOC));
}
In summary, use different placeholders with the same criteria.
Related
I have a table in my database with the structure something like that:
ID,category,county,keyword
Now I have the category, the county and some text.
I want to take from my database the rows with the category, county that I have, BUT also the 'keyword' must be in my text
select from `posts` where `category`='$category' and `county`='$county'
Can anyone help me with the third condition ('keyword' must be in my $text)?
Thank you!
If you mean the text of column keyword should be contained anywhere in the $text variable, then use
select ID,category,county,keyword
from `posts`
where `category`='$category' and `county`='$county'
and '$text' like concat('%', `keyword`, '%')
Actually, to prevent SQL injection, it would be better to use a prepared statement using parameters for $category, $county, and $text to prevent SQL injection.
In essence, I have a value that I have to call a couple times in my SQL query. Thus, is it possible to reuse the same named placeholder in the statement e.g.
SELECT :Param FROM Table WHERE Column = :Param, then simply bindValue(":Param"), and have the value be there for both :Params?
PDO::prepare states that "you cannot use a named parameter marker of the same name twice in a prepared statement", so I guess that's a no then.
You can if you set PDO::ATTR_EMULATE_PREPARES = true.
E.g. $connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);.
If you're using Laravel you can set this in an options array in config/database.php. e.g. PDO::ATTR_EMULATE_PREPARES => true
Apart from reuse, the main issue here is that you are trying to dynamically change col names.
This answer posted by an anonymous user on http://php.net/manual/en/pdo.prepare.php :
To those wondering why adding quotes to around a placeholder is wrong,
and why you can't use placeholders for table or column names:
There is a common misconception about how the placeholders in prepared
statements work: they are not simply substituted in as (escaped)
strings, and the resulting SQL executed. Instead, a DBMS asked to
"prepare" a statement comes up with a complete query plan for how it
would execute that query, including which tables and indexes it would
use, which will be the same regardless of how you fill in the
placeholders.
The plan for "SELECT name FROM my_table WHERE id = :value" will be the
same whatever you substitute for ":value", but the seemingly similar
"SELECT name FROM :table WHERE id = :value" cannot be planned, because
the DBMS has no idea what table you're actually going to select from.
Even when using "emulated prepares", PDO cannot let you use
placeholders anywhere, because it would have to work out what you
meant: does "Select :foo From some_table" mean ":foo" is going to be a
column reference, or a literal string?
When your query is using a dynamic column reference, you should be explicitly white-listing the columns you know to exist on the table, e.g. using a switch statement with an exception thrown in the default: clause.
Many queries like yours can be rewritten to use only one placeholder.
SELECT :Param FROM Table WHERE Column = :Param
would be the same as
SELECT Column FROM Table WHERE Column = :Param
But sometimes it's not that simple. For example:
SELECT *
FROM my_table
WHERE first_name LIKE :Param
OR last_name LIKE :Param
OR biography LIKE :Param
In such case you could reuse the parameter value storing it in a cross joined derived table (subquery in FROM clause):
SELECT t.*
FROM my_table t
CROSS JOIN (SELECT :Param as Param) AS x
WHERE first_name LIKE x.Param
OR last_name LIKE x.Param
OR biography LIKE x.Param
There's a workaround:
function search($criteria) {
$sql = "SELECT * FROM my_table
WHERE column_1 like CONCAT('%', :criteria1, '%')
OR column_2 like CONCAT('%', :criteria2, '%')
OR column_3 like CONCAT('%', :criteria3, '%')
";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':criteria1', $criteria);
$stmt->bindParam(':criteria2', $criteria);
$stmt->bindParam(':criteria3', $criteria);
$stmt->execute();
return($stmt->fetchAll(PDO::FETCH_ASSOC));
}
In summary, use different placeholders with the same criteria.
I am trying to to get my data from MYSQL using PDO but I am not having any luck.
Here is what I have tried:
$postQuery = $DBH->prepare("SELECT title, views, rating, thumb FROM posts WHERE category=:category and status=1 ORDER BY :sort DESC");
$postQuery ->bindParam(':category', $category);
$postQuery ->bindParam(':sort', $sort);
$postQuery ->execute();
This works without errors but it returns all of the posts in alphabetical order, ignoring the category and the sort.
I tried this:
$postQuery = $DBH->query("SELECT title, views, rating, thumb FROM posts WHERE category={$category} and status=1 ORDER BY {$sort} DESC");
This did work but I don't get the protection of the prepared statement. Any ideas on why one statement works but the other one does not?
Your bound parameter :sort gets expanded to a string literal, not a SQL identifier. That is, you are effectively evaluating something along the lines of:
ORDER BY 'rating' DESC
Since literals like this are constant for every record, it has no effect on the order of the resultset.
You can't parameterise identifiers, so must concatenate that part of the SQL into your prepared statement (the safest way is to set $sort from a predetermined set of safe values, based on whatever logic is appropriate to your needs).
You can't use place holders in ORDER BY clauses. See this question: How do I set ORDER BY params using prepared PDO statement?
I'm doing some queries into MySQL database using MySQLi extension in PHP. I need to do a query like this:
SELECT col1, col2 FROM mytable WHERE id IN (2, 4, 6);
This is a concrete example of the query, but the number of ids inside the parenthesis after the IN keyword will vary. Since I want to use the mysqli->prepare() function (prepared statement) I would do it like this:
$statement->prepare("SELECT col1, col2 FROM mytable WHERE id IN (?)");
$statement->bind_pararm("s".... something)
I'm not sure what to put in the bind param function, since i cant know how many IDs will actually be queried for. I couldn't find any examples how to use IN keyword inside prepared statements.
If you prepare your statement once you need it, you could create the placeholders like
join(",", array_fill(0, count($ids) , "?"))
or something.
I am trying to use a prepared statement where the column is prepared
i.e.
SELECT ? FROM users
Now this normally works if I put
SELECT id FROM users
But doing the first statement, the value is the column name.
id = id
0 = 0
What am I doing wrong, or is this possible?
No you can't bind column names or table names.
Here's more info Escaping column names in PDO statements
A prepared statement can only replace value in the statement not field nor column name, this is because prepared statement are kind of precompiled and optimized in function of the whole statement except the value.
so this is possible:
SELECT id FROM users WHERE name=?
but not this:
SELECT ? FROM users WHERE name='john'
SELECT id FROM ? WHERE name='john'
SELECT id FROM users WHERE ?='john'