I am in search of a way to use wildcards with the following mysql query.
public function getSetsOnMonth($setsId, $setsDate, $offset, $count)
{
$sql = sprintf("SELECT * FROM " . $this->_prefix . "media_set AS f
INNER JOIN " . $this->_prefix . "media_set_sets_assoc AS fs
ON fs.set_id = f.set_id AND fs.sets_id = '%s'
WHERE f.posted LIKE '%s'
AND f.is_active = 1
ORDER BY f.set_id DESC
LIMIT %s, %s",
mysql_real_escape_string($setsId),
mysql_real_escape_string($setsDate),
mysql_real_escape_string($offset),
mysql_real_escape_string($count));
echo $sql; exit;
$rs = mysql_query($sql);
$rows = array();
while ($row = mysql_fetch_object($rs)) {
$rows[] = $row;
}
mysql_free_result($rs);
return new XXX_Model_RecordSet($rows, $this);
}
What i am looking to do is by month so that would be (where f.posted LIKE '%s') is located
I have tried using the % in various ways and it always errors.
For instance (where f.posted LIKE '%s %') returns this error (Warning: sprintf(): Too few arguments in..).
I have also tried to use (where f.posted LIKE '$setsDate%') this does not return a sql error, however it makes my query limit the same as the date and the single quote is removed after the % sign in the sql printout.
oddly if i reverse and put the percent sign in front of $setsDate (%setsDate) is cancels out everything after the % and shows everything.
any help would be appreciated.
UPDATED 9:35 EST 03/10
Here is the sql output ('%s %%'):
SELECT * FROM media_set AS f INNER JOIN media_set_sets_assoc AS fs ON fs.set_id = f.set_id AND fs.sets_id = '1' WHERE f.posted LIKE '201312 %' AND f.is_active = 1 ORDER BY f.set_id DESC LIMIT 0, 18
Here is SQL output ('%s%%'):
WHERE f.posted LIKE '201312%'
Notice no space between.
I believe you should be escaping the percentage sign for use in a LIKE statement, as such:
WHERE f.posted LIKE '%s %%'
Your problem is that sprintf treats % as a special character, so it gets confused when you want an actual % character. The solution for that is to use %% when you want the actual % character.
However, please do not insert values into your SQL with sprintf. That is an extremely bad practice and it is responsible for most of the security vulnerabilities in PHP code. Escaping your strings is not good enough. Use parameterized queries instead.
You should use the PDO or mysqli extensions, which support parameterized queries.
There are many articles explaining why you should not splice values into your SQL. Here is one: http://blog.codinghorror.com/give-me-parameterized-sql-or-give-me-death/
The way you should be doing it is discussed here: https://stackoverflow.com/a/60496/219155
Related
I'm trying to get mySQL to send me posts between two dates. Since the dates are user input I use Prepared Statements. The query I use looks like this:
SELECT * FROM butiken_orderregister
WHERE datum_skapad BETWEEN :datum_skapad_0 AND :datum_skapad_1
ORDER BY datum_skapad LIMIT 9999;
:datum_skapad_0 = 2014-10-20
:datum_skapad_1 = 2014-10-23
The column datum_skapad is in date format.
The query runs fine, but only returns posts where the column matches :datum_skapad_1. However, when I use MySQL Workbench I can get the results I want by using the query:
SELECT * FROM butiken_orderregister
WHERE datum_skapad BETWEEN "2014-10-20" AND "2014-10-23"
ORDER BY datum_skapad LIMIT 9999;
To me, these should be equivalent, but I evidently don't get the same results. I've tried using parenthesis like this:
SELECT * FROM butiken_orderregister
WHERE (datum_skapad BETWEEN :datum_skapad_0 AND :datum_skapad_1)
ORDER BY datum_skapad LIMIT 9999;
but it changes nothing.
Is there an obvious mistake I've made here or could the problem lie somewhere else?
Addendum:
This is the PHP that takes care of binding the parameters:
$stmt = $con->prepare($sql);
foreach ($flat_data as $field => $value) {
$stmt->bindParam(':' . $field, $value);
$out .= ':' . $field . ' = ' . $value . "\n";
}
if ($return_query) {
$out = $sql . "\n\n" . $out . "\n\n";
} else {
try {
$stmt->execute();
$out = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch(PDOException $error) {
$out = $error->getMessage();
}
}
$flat_data is an array with all values and the names I've used for them in the SQL. $con is a PDO object.
The following comment by Ryan Vincent solved my issue:
I suggest that you try using 'bindValue' rather than 'bindParam': questions/1179874/pdo-bindparam-versus-bindvalue – Ryan Vincent Oct 23 at 17:06
If you write your comment as an answer, Ryan, I can mark it as the accepted one. Until then I'll mark this one.
I really can't find that I'm changing the variables between my binding and my execution though, but since this solved my issue I suppose it is just one of those PHP quirks. It's a strange and wonderful language...
I agree with #exussum, check the variables. Also check the column types and date format used.
Maybe you could try explicitly converting the col and parameters to dates, are you using date or datetime?
SELECT * FROM butiken_orderregister
WHERE (
datum_skapad BETWEEN
DATE(:datum_skapad_0) AND DATE(:datum_skapad_1)
)
ORDER BY datum_skapad LIMIT 9999;
another way to cast dates:
SELECT * FROM butiken_orderregister
WHERE (
datum_skapad BETWEEN
CAST(:datum_skapad_0 AS DATE) AND CAST(:datum_skapad_1 AS DATE)
)
ORDER BY datum_skapad LIMIT 9999;
Maybe the problem is with how PHP handles the parameters, you should post the PHP code where you do this.
Might not resolve the issue but I would avoid using BETWEEN.
Instead use (for your example) Date >= '2014-10-20' and Date < '2014-10-24'
More info on why
I have a sql query that is generated using php. It returns the surrogate key of any record that has fields matching the search term as well as any record that has related records in other tables matching the search term.
I join the tables into one then use a separate function to retrieve a list of the columns contained in the tables (I want to allow additions to tables without re-writing php code to lower ongoing maintenance).
Then use this code
foreach ($col_array as $cur_col) {
foreach ($search_terms_array as $term_searching) {
$qry_string.="UPPER(";
$qry_string.=$cur_col;
$qry_string.=") like '%";
$qry_string.=strtoupper($term_searching);
$qry_string.="%' or ";
}
}
To generate the rest of the query string
select tbl_sub_model.sub_model_sk from tbl_sub_model inner join [about 10 other tables]
where [much code removed] or UPPER(tbl_model.image_id) like '%HONDA%' or
UPPER(tbl_model.image_id) like '%ACCORD%' or UPPER(tbl_badge.sub_model_sk) like '%HONDA%'
or UPPER(tbl_badge.sub_model_sk) like '%ACCORD%' or UPPER(tbl_badge.badge) like '%HONDA%'
or UPPER(tbl_badge.badge) like '%ACCORD%' group by tbl_sub_model.sub_model_sk
It does what I want it to do however it is vulnerable to sql injection. I have been replacing my mysql_* code with pdo to prevent that but how I'm going to secure this one is beyond me.
So my question is, how do I search all these tables in a secure fashion?
Here is a solution that asks the database to uppercase the search terms and also to adorn them with '%' wildcards:
$parameters = array();
$conditions = array();
foreach ($col_array as $cur_col) {
foreach ($search_terms_array as $term_searching) {
$conditions[] = "UPPER( $cur_col ) LIKE CONCAT('%', UPPER(?), '%')";
$parameters[] = $term_searching;
}
}
$STH = $DBH->prepare('SELECT fields FROM tbl WHERE ' . implode(' OR ', $conditions));
$STH->execute($parameters);
Notes:
We let MySQL call UPPER() on the user's search term, rather than having PHP call strtoupper()
That should limit possible hilarious/confounding mismatched character set issues. All your normalization happens in one place, and as close as possible to the moment of use.
CONCAT() is MySQL-specific
However, as you tagged the question [mysql], that's probably not an issue.
This query, like your original query, will defy indexing.
Try something like this using an array to hold parameters. Notice % is added before and after term as LIKE %?% does not work in query string.PHP Manual
//Create array to hold $term_searching
$data = array();
foreach ($col_array as $cur_col) {
foreach ($search_terms_array as $term_searching) {
$item = "%".strtoupper($term_searching)."%";//LIKE %?% does not work
array_push($data,$item)
$qry_string.="UPPER(";
$qry_string.=$cur_col;
$qry_string.=") LIKE ? OR";
}
}
$qry_string = substr($qry_string, 0, -3);//Added to remove last OR
$STH = $DBH->prepare("SELECT fields FROM table WHERE ". $qry_string);//prepare added
$STH->execute($data);
EDIT
$qry_string = substr($qry_string, 0, -3) added to remove last occurrence of OR and prepare added to $STH = $DBH->prepare("SElECT fields FROM table WHERE". $qry_string)
I have been using the PHP function strpos to find results containing the characters of a string from a DB:
User Types: Hel
Results: Hello, Hell, Helli, Hella
I have it basically query the entire table:
$result = mysql_query("SELECT * FROM Events");
And then ran a while statement to see which of the results contain the characters of the input:
while($row = mysql_fetch_array($result))
{
$pos = strpos($row['Title'], $q);
if ($pos === false) {
} else {
echo $row['Title'];
}
}
And to find the number of results, I was using:
$n = $n++
Inside of the while statement.
I know you can use:
$num_rows = mysql_num_rows($result);
To find the number of results if you are only selecting those values from the database, but do I have to use this while statement to find the number of results that match the strpos function? Or can I put the strpos in to the Select From query?
Any help is greatly appreciated,
Taylor
This seems highly inefficient. Why wouldn't you simply let the database do the searching for you?
$result = mysql_query("SELECT * FROM Events WHERE Title LIKE '" . addslashes($q) . "%'");
Then just loop through the results.
You could update your SQL to something like
SELECT *
FROM Events
WHERE Title LIKE '{your_string}%'
Make sure to filter for sql injection though.
You can use the LIKE statement:
SELECT * FROM Events WHERE field1 LIKE '%something%'
Where the special % characters say "Anything of any length"; so we're searching for something (or nothing), then the string, then something (or nothing.) For example, searching for %f% will match foo, off, and affirmative.
Just as general advice, I recommend that you use php's MySQLi class; it's an improved version (hence the i), and provides prepared statements, so you won't have to worry too much about SQL injections.
I am using a query inside PHP as:
$query = 'SELECT * from #__chronoforms_UploadAuthor where text_6 like "%'.$_GET['title'].'%" and text_7 like "%'.$_GET['author'].'%" limit 0,1';
Where I am trying to insert a PHP variable instead of 1 in the limit..
$query = 'SELECT * from #__chronoforms_UploadAuthor where text_6 like "%'.$_GET['title'].'%" and text_7 like "%'.$_GET['author'].'%" limit 0,"'.$_GET['limit'].'"';
but it shows me an error. There are some errors in keeping $_GET['limit'].
Three things:
The way you're writing out those queries is a bit hard to read. Personally I prefer using a multi-line heredoc syntax (as per below), but this isn't strictly required;
Any user input should go through mysql_real_escape_string() to avoid SQL injection attacks. Note: "user input" includes anything that comes from the client including cookies, form fields (normal or hidden), query strings, etc.; and
You don't need to quote the second argument to LIMIT clause, which is probably the source of your problem, meaning put LIMIT 0,5 not LIMIT 0,"5".
So try:
$title = mysql_real_escape_string($_GET['title']);
$author = mysql_real_escape_string($_GET['author']);
$limit = (int)$_GET['limit'];
$query = <<<END
SELECT *
FROM #__chronoforms_UploadAuthor
WHERE text_6 LIKE "$title%"
AND text_7 LIKE "%$author%"
LIMIT 0,$limit
END;
Also, one commentor noted that % and _ should be escaped. That may or may not be true. Many applications allow the user to enter wildcards. If that's the case then you shouldn't escape them. If you must escape them then process them:
$title = like_escape($limit);
function like_escape($str) {
return preg_replace('!(?|\\)((?:\\)*)([%_])!', '$1\$2', $str);
}
That somewhat complicated regular expression is trying to stop someone putting in '\%' and getting '\%', which then escape the backslash but not the '%'.
The hash sign (#) starts a comment in SQL, which looks like your problem
Want bunch of awful answers!
a. To solve the limit problem:
$limit = intval($_GET['limit']);
and then
...LIMIT 0, $limit
in the query.
b. To sanitize $_GET['title'], as many mentioned:
$title = mysql_real_escape_string($_GET['title']);
So the final code must be
$limit=intval($_GET['limit']);
$title = mysql_real_escape_string($_GET['title']);
$author = mysql_real_escape_string($_GET['author']);
$query = "SELECT * from #__chronoforms_UploadAuthor
WHERE text_6 like '$title' and text_7 like '%$author%'
LIMIT 0, $limit";
You've enclosed the $_GET['limit'] in double-quotes, which is the source of the problem.
Try this:
$query = 'SELECT * from #__chronoforms_UploadAuthor where text_6 like "%'.$_GET['title'].'%" and text_7 like "%'.$_GET['author'].'%" limit 0,'.$_GET['limit'];
Also as Cletus mentions in this answer, there are many, more serious problems you need to resolve.
Remove the double-quotes around $_GET['limit']. The two numbers that the LIMIT clause takes should not be quoted.
This should work:
$query = 'SELECT * from #__chronoforms_UploadAuthor where text_6 like "%'.$_GET['title'].'%" and text_7 like "%'.$_GET['author'].'%" limit 0,'.$_GET['limit'];
But you really should filter incoming data...
$query = 'SELECT * from #__chronoforms_UploadAuthor where text_6 like "%'.mysql_real_escape_string($_GET['title']).'%" and text_7 like "%'.mysql_real_escape_string($_GET['author']).'%" limit 0,"'.intval($_GET['limit']).'"';
Is there any way to check if a column is "anything"? The reason is that i have a searchfunction that get's an ID from the URL, and then it passes it through the sql algorithm and shows the result. But if that URL "function" (?) isn't filled in, it just searches for:
...AND column=''...
and that doesn't return any results at all. I've tried using a "%", but that doesn't do anything.
Any ideas?
Here's the query:
mysql_query("SELECT * FROM filer
WHERE real_name LIKE '%$searchString%'
AND public='1' AND ikon='$tab'
OR filinfo LIKE '%$searchString%'
AND public='1'
AND ikon='$tab'
ORDER BY rank DESC, kommentarer DESC");
The problem is "ikon=''"...
and ikon like '%' would check for the column containing "anything". Note that like can also be used for comparing to literal strings with no wildcards, so, if you change that portion of SQL to use like then you could pre-set the variable to '%' and be all set.
However, as someone else mentioned below, beware of SQL injection attacks. I always strongly suggest that people use mysqli and prepared queries instead of relying on mysql_real_escape_string().
You can dynamically create your query, e.g.:
$query = "SELECT * FROM table WHERE foo='bar'";
if(isset($_GET['id'])) {
$query .= " AND column='" . mysql_real_escape_string($_GET['id']) . "'";
}
Update: Updated code to be closer to the OP's question.
Try using this:
AND ('$tab' = '' OR ikon = '$tab')
If the empty string is given then the condition will always succeed.
Alternatively, from PHP you could build two different queries depending on whether $id is empty or not.
Run your query if search string is provided by wrapping it in if-else condition:
$id = (int) $_GET['id'];
if ($id)
{
// run query
}
else
{
// echo oops
}
There is noway to check if a column is "anything"
The way to include all values into query result is exclude this field from the query.
But you can always build a query dynamically.
Just a small example:
$w=array();
if (!empty($_GET['rooms'])) $w[]="rooms='".mysql_real_escape_string($_GET['rooms'])."'";
if (!empty($_GET['space'])) $w[]="space='".mysql_real_escape_string($_GET['space'])."'";
if (!empty($_GET['max_price'])) $w[]="price < '".mysql_real_escape_string($_GET['max_price'])."'";
if (count($w)) $where="WHERE ".implode(' AND ',$w); else $where='';
$query="select * from table $where";
For your query it's very easy:
$ikon="";
if ($id) $ikon = "AND ikon='$tab'";
mysql_query("SELECT * FROM filer
WHERE (real_name LIKE '%$searchString%'
OR filinfo LIKE '%$searchString%')
AND public='1'
$ikon
ORDER BY rank DESC, kommentarer DESC");
I hope you have all your strings already escaped
I take it that you are adding the values in from variables. The variable is coming and you need to do something with it - too late to hardcode a 'OR 1 = 1' section in there. You need to understand that LIKE isn't what it sounds like (partial matching only) - it does exact matches too. There is no need for 'field = anything' as:
{field LIKE '%'} will give you everything
{field LIKE 'specific_value'} will ONLY give you that value - it is not partial matching like it sounds like it would be.
Using 'specific_value%' or '%specific_value' will start doing partial matching. Therefore LIKE should do all you need for when you have a variable incoming that may be a '%' to get everything or a specific value that you want to match exactly. This is how search filtering behaviour would usually happen I expect.