sql field value is contained by string - php

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.

Related

Uncaught mysqli_sql_exception: Unknown column in 'where clause' [duplicate]

This question already has answers here:
How to include a PHP variable inside a MySQL statement
(5 answers)
Closed 1 year ago.
I have a problem with my php code when i try to choose a string in my database.
Well, I have a table "news" and a field "category" (TINYTEXT type) there. And I try to display a string from this table by using "where clause":
$Sql = 'SELECT * FROM `news` WHERE `category` = '.$Param['category'].' ORDER BY `id` DESC';
I must say that I received "$Param['category']" by using URL Path: 1
So, if my "category" value of any string in table has any string type value like "games" 2, 3 - string from my table isn't displayed with this error "Fatal error: Uncaught mysqli_sql_exception: Unknown column 'games' in 'where clause'"
But, if I change the "category" value of string in the database into anything numeral value - the string from my table is displayed correctly!4, 5
What's wrong with my code? Thank you in advance! Sorry for the mistakes.
You need to use prepared queries, both for the sake of SQL injection protection, and because it will fix your bug. I believe the issue is that you don't have any quotes at all around your parameters. As a result you're building queries like this:
SELECT * FROM `news` WHERE `category` = games ORDER BY `id` DESC
SELECT * FROM `news` WHERE `category` = 1 ORDER BY `id` DESC
The latter is a valid query: mysql looks for records where the category is 1. The former is not: the query tries to find records where the column category matches the column games. The latter doesn't exist so you get an error. You want to build these queries:
SELECT * FROM `news` WHERE `category` = 'games' ORDER BY `id` DESC
SELECT * FROM `news` WHERE `category` = '1' ORDER BY `id` DESC
Which you would do with this code:
$Sql = "SELECT * FROM `news` WHERE `category` = '".$Param['category']."' ORDER BY `id` DESC";
(note that I switched to double quotes and included single quotes around the input parameter). BUT THIS IS STILL WRONG as it leaves you extremely vulnerable to SQL Injection, which would allow an attacker to potentially download or modify your entire database. Take the time to read up and learn how to use prepared queries, which is both more secure and also would have prevented this bug in the first place.
Others have suggested you use parameterized queries. That's good advice, but they didn't show you what it looks like (not that it's hard to find examples).
Here's a quick example using PDO:
$Sql = 'SELECT * FROM `news` WHERE `category` = ? ORDER BY `id` DESC';
$stmt = $pdo->prepare($Sql);
$stmt->execute([ $Param['category' ]);
This executes the query as if you had put $Param['category'] into the SQL, but it does it completely safely, even if the value contains special characters.
You don't put quotes around the ? placeholder. It's implied that the query will treat it as a single scalar value, not a column name. In fact, you can use parameters only for values — not for column names, or expressions, or SQL keywords or anything else.
Writing code using query parameters is easier than not using parameters, because it makes your code easier to write and easier to read.
You have to put quotes around any string in the query if it is not a field in the table. Try this:
$Sql = "SELECT * FROM `news` WHERE `category` = '".$Param['category']."' ORDER BY `id` DESC";
As was mentioned if you are pulling in that string from a source where you have to word about code injection you should use prepared statements or PDO.

WHERE NOT EXISTS syntax error

I have this code that does not work, and im not sure why...
if(isset($_GET['id'], $_SESSION['username'])){
$id = $_GET['id'];
$user = $_SESSION['username'];
$query = $handler->query("INSERT INTO photolikes('User', 'Photo')
SELECT '$user', '$id'
WHERE NOT EXISTS (SELECT Id FROM photolikes WHERE User='$user' AND Photo=$id)");
}else{
}
Is just supposed to insert user and photo into a table if there is no such in there before... thanks for any help!
The SELECT is missing the FROM clause which is required when a WHERE clause is used.
That's the problem.
There's a couple of ways to fix it.
For a quick fix, you can add FROM DUAL before the WHERE.
If you don't like your MySQL queries looking like they are Oracle queries, you can use an inline view as a rowsource.
In place of FROM DUAL you could use FROM (SELECT 1) i.
That's the less-Oracle-more-MySQL-like way of fixing it. That's how I would do it.
You could also reference any table or view that you are guaranteed returns exactly one row. (It can't be zero rows, and it can't be two rows.
A couple other notes:
In MySQL, identifiers (for example column names) can be escaped with backtick characters, but not single quotes. Identifiers only need to be escaped if they contain characters that aren't allowed (in unescaped identifiers) or if the identifier conflicts with a reserved word.
INSERT INTO photolikes(`User`, `Photo`)
^ ^ ^ ^
Also, the code appears to be vulnerable to SQL Injection. Potentially unsafe values that are incorporated into the text of a SQL statement should be properly escaped. But an even better pattern is to use prepared statements with bind placeholders.
INSERT INTO photolikes(`User`, `Photo`)
SELECT '$user', '$id'
FROM <someTable>
^^^^ you miss the FROM
WHERE NOT EXISTS (SELECT Id
FROM photolikes -- Here you didnt forget.
WHERE User='$user' AND Photo=$id)")

It is not updating the row in table PDO [duplicate]

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.

How to write a like query in PHP

I have a PHP string with a like query - something like this:
$select = "select category_id, category_name from categories where category_name like %"'.$category.'"%;
But I am pretty sure this is totally wrong, and I am getting confused in the million quotes :)
Whats the right way to put this query together?
Thank you!!
You want the % wildcards in the string, not outside it.
Also, If you want to evaluate the php variable in the string, you should brace the variable.
" select category_id, category_name from categories where category_name like '%{$category}%' "
or concatenate it
" select category_id, category_name from categories where category_name like '%" . $category . "%' "
If you're looking for "best practices", you should look into prepared statements using the mysqli class. That being said,
Your quotes are backwards around the $category variable (it's "', it should be '")
The %'s in your statement should be inside the quotes ('), not outside them
I'm pretty sure it's just:
$select = "select category_id, category_name from categories
where category_name like '%$category%'";
You can use positional parameters. Not sure what database server you're using, but say you're using the PDO layer, then have a look at PDO::prepare.
It would be better to do it a different way but to answer the question, if you use double quotes you not need to concatenate the variable with the dot
$select = "select category_id, category_name from categories where category_name like '%{$category}%'";
You messed up the quotes
"select [...] like '%".$category."%'";
That will become
select [...] like '%something%'
But you should use query parameters, to avoid sql injections
This will be relatively functional:
$select = "select category_id, category_name from categories where category_name like '%"
.mysql_real_escape_string($category)."%'";
However, you'll probably want to additionally escape characters that have special meaning inside of a LIKE, including %
Note also that mysql will not be able to use an index to optimize this query, meaning it won't perform on large databases. You may wish to investigate myISAM fulltext indexes for text search over a large dataset.

PDO Parameterized Query - Reuse named placeholders?

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.

Categories