So I am using PHP and SQL (PDO) to write a query that would take values from an array of check box's (names[]) so for instance someone could submit the values "Bob", "John" and "Bill" and I want to match those with a column in the database.
Problem: Database is an import from Excel spreadsheet used for years and there is no set format for the column I match with, some examples of values stored the database could look like any of the following:
"Bill & Bob"
Bill
John and Bill
"Bill"
John, Bill, and Bob
John will perform the task
As you can see I need to use Like %value% to match values because The post request will send just Bill, John and Bob. My problem is how do I do it with the array passed because it could be just John or it could be 2 or all 3, but you can't use something like WHERE column LIKE in(array) correct? Would my best bet be to run the query once for each name and append the results to a single array? I know I could make some sort of dynamic statement that is something like:
WHERE column LIKE '%Bob%' OR
column LIKE '%John%' OR
column LIKE '%Bill%';
My problem doing it that way is that the above method is susceptible to SQL injection if the input isn't satisfied correct? Even if I use a bindValue() and make each one a bindable variable then I have to figure out how many binds there are and I feel like if I have to add names in the future it would be more difficult than it should be. Is the best way to do it what I said above about one query executed multiple times for, once for each name and then append the results together?
Thanks in advance.
EDIT:
Driver is PDO, Engine is MSSQL
You can do it like this.
//Prepare an array with names
$names = [
':name_1' => '%Bob%',
':name_2' => '%Bill%',
];
//build the where
$whereQuery = '';
foreach($names as $bind => $value) {
if(empty($whereQuery)) $whereQuery .= ' OR ';
$whereQuery .= ' column LIKE '.$bind;
}
//here is missing the code with SQL and pdo preparing query
//after preparing query just execute with $names
$sth->execute($names);
Be careful this example provide only code helping you to figure out the solution.
Hope this helps
You should definitely normalize your database.
But just quick approach and since php was mentioned you can generate query like
$names = ['John','Bob','Bill'];
$query = 'SELECT * FROM my_weird_table WHERE ';
$first = true;
foreach ($names as $name) {
if ($first) {
$query .= '`column` LIKE "%'.$name.'%" ';
$first = false;
} else {
$query .= 'OR `column` LIKE "%'.$name'.'%" ';
}
}
Related
I'm running a PDO query, something like:
$inputArr = array(val1, val2, val3, ...);
$qMarks = str_repeat('?,', count($inputArr) - 1) . '?';
$stmt = $db->prepare("SELECT id, name, type, level
FROM table
WHERE name IN ($qMarks)");
$stmt->execute($inputArr);
... parse the rows that have been returned
And this works exactly as expected, no hang-ups or anything.
My problem is that I need to know which value from $inputArr was used to get each row returned.
I've tried
WHERE name IN ($qMarks) AS inputVal
and
WHERE name IN ($qMarks AS inputVal)
but those crash the query.
How can I determine which input array value was used to return each row in the output?
EDIT 1
Yes, I understand that the input search value would be name, for this particular case, but the query above is only for demonstration purposes of how I am putting the search values into the query.
The actual is much more complex, and returns any name value with is close (but not always identical).
The AS keyword is not going to work as you expect it. It's mainly used for aliasing subqueries. You can't (to my knowledge) use it in a WHERE clause.
The scenario you've outlined should have the 'name' in $row['name']. If it was a different variable that you wanted to see, you'd simply add it in your SELECT clause.
Great question, and simple answer:
The WHERE name IN $qMarks)"); part of your code is only obtaining the values in your database that are matching your array, so what you can do is see which values of name are present in the row you fetched. For example:
$rows_fetched = $stmt->fetchAll(PDO::FETCHASSOC);
$inputArray = array();
foreach($rows_fetched as $value)
{
$inputArray[] = $value['name'];
}
print_r($inputArray);//printing the results
Now you have the array $inputArray with all the values used to return each row in the output. Let me know if that worked for you!
I try to make query to database, base on user input, because I have multiple inputs in single form it's a big more complicated.
I decide to check if input is filled like:
$query = "";
if((trim($searchParams->firstname)))
$query .= "firstname,";
which works correctly for me, and after I check each input I explode string to array by , and foreach it
$querys = explode(",", $query);
foreach($querys as $q)
{
...
}
What basically create an array of all filled inputs, however just their names base on what i need to get values as well.
So I try to add something like this to query
$searchParams->$q;
But this doesn't work (say it's empty despite the fact if i echo it before it's filled)
I try to do something really dirt as:
$param = "$searchParams->".$q;
$values .= $param;
which say
Object of class could not be converted to string ( if i var_dump($q) it say its string so i dont understand)
I think I 'm doing it really badly but thats the only thing I found out as potentially useful. Any advise to fix this will be helpful.
p.s. Here is how final query should looks like:
"SELECT * FROM candidates WHERE firstname = ? AND surname = ?" ,$searchParams->firstname, $searchParams->surname
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 am looking to use the contents of an array
$arr =array(24,28,30,34, 40);
and pass these into the where clause of a MySQL select statement, all my research has shown this done by using IN to pass in all the array values in one go.
I need to pass in each array element one at a time and then echo out the results of the SQL statement one at a time as the select statement is updated with the next array element.
New to programming and PHP so just need a little example to get me started...
Thanks to Zad highlighted the real issue
I need to pass each array value individually to a SQL statement as these need to be utilised in Where clause with BETWEEN, eg. WHERE age BETWEEN $array1 AND $array2 in order to determine count over an age range
thanks for all the input
You can use the implode function to build the string that contains the list;
$arr =array(24,28,30,34, 40);
$query = 'SELECT * FROM mytable WHERE id IN (' .implode($arr, ', '). ' )';
echo $query;`
http://codepad.org/tLPZxq8P
http://mx2.php.net/manual/en/function.implode.php
try it with escaping the argument
foreach($arr as $array_element) {
$query = 'SELECT * FROM table WHERE field = \''.mysql_real_escape_string($array_element).'\'';
//your statement
}
You can use a foreach function:
// make connection to mysql server
foreach ( $arr as $element ) {
$statement = "SELECT whatever FROM wherever WHERE something = $element"; // maybe a little validation here wouldn't hurt either
// execute statement
// process results
} // end of foreach
// close connection
$arr =array(24,28,30,34, 40);
$a = 'SELECT * FROM foo WHERE bar IN('.implode(',',$arr).')';
Edit: I'll admit, I didn't fully read the question, the title is misleading - consider changing that.
I need to pass in each array element one at a time and then echo out
the results of the SQL statement one at a time as the select statement
is updated with the next array element.
Could you explain how the scenario a bit better?
I'm trying to write a simple, full text search with PHP and PDO. I'm not quite sure what the best method is to search a DB via SQL and PDO. I found this this script, but it's old MySQL extension. I wrote this function witch should count the search matches, but the SQL is not working. The incoming search string look like this: 23+more+people
function checkSearchResult ($searchterm) {
//globals
global $lang; global $dbh_pdo; global $db_prefix;
$searchterm = trim($searchterm);
$searchterm = explode('+', $searchterm);
foreach ($searchterm as $value) {
$sql = "SELECT COUNT(*), MATCH (article_title_".$lang.", article_text_".$lang.") AGINST (':queryString') AS score FROM ".$db_prefix."_base WHERE MATCH (article_title_".$lang.", article_text_".$lang.") AGAINST ('+:queryString')";
$sth = $dbh_pdo->prepare($sql);
$sql_data = array('queryString' => $value);
$sth->execute($sql_data);
echo $sth->queryString;
$row = $sth->fetchColumn();
if ($row < 1) {
$sql = "SELECT * FROM article_title_".$lang." LIKE :queryString OR aricle_text_".$lang." LIKE :queryString";
$sth = $dbh_pdo->prepare($sql);
$sql_data = array('queryString' => $value);
$sth->execute($sql_data);
$row = $sth->fetchColumn();
}
}
//$row stays empty - no idea what is wrong
if ($row > 1) {
return true;
}
else {
return false;
}
}
When you prepare the $sql_data array, you need to prefix the parameter name with a colon:
array('queryString' => $value);
should be:
array(':queryString' => $value);
In your first SELECT, you have AGINST instead of AGAINST.
Your second SELECT appears to be missing a table name after FROM, and a WHERE clause. The LIKE parameters are also not correctly formatted. It should be something like:
sql = "SELECT * FROM ".$db_prefix."_base WHERE article_title_".$lang." LIKE '%:queryString%' OR aricle_text_".$lang." LIKE '%:queryString%'";
Update 1 >>
For both SELECT statements, you need unique identifiers for each parameter, and the LIKE wildcards should be placed in the value, not the statement. So your second statement should look like this:
sql = "SELECT * FROM ".$db_prefix."_base WHERE article_title_".$lang." LIKE :queryString OR aricle_text_".$lang." LIKE :queryString2";
Note queryString1 and queryString2, without quotes or % wildcards. You then need to update your array too:
$sql_data = array(':queryString1' => "%$value%", ':queryString2' => "%$value%");
See the Parameters section of PDOStatement->execute for details on using multiple parameters with the same value. Because of this, I tend to use question marks as placeholders, instead of named parameters. I find it simpler and neater, but it's a matter of choice. For example:
sql = "SELECT * FROM ".$db_prefix."_base WHERE article_title_".$lang." LIKE ? OR aricle_text_".$lang." LIKE ?";
$sql_data = array("%$value%", "%$value%");
<< End of Update 1
I'm not sure what the second SELECT is for, as I would have thought that if the first SELECT didn't find the query value, the second wouldn't find it either. But I've not done much with MySQL full text searches, so I might be missing something.
Anyway, you really need to check the SQL, and any errors, carefully. You can get error information by printing the results of PDOStatement->errorCode:
$sth->execute($sql_data);
$arr = $sth->errorInfo();
print_r($arr);
Update 2 >>
Another point worth mentioning: make sure that when you interpolate variables into your SQL statement, that you only use trusted data. That is, don't allow user supplied data to be used for table or column names. It's great that you are using prepared statements, but these only protect parameters, not SQL keywords, table names and column names. So:
"SELECT * FROM ".$db_prefix."_base"
...is using a variable as part of the table name. Make very sure that this variable contains trusted data. If it comes from user input, check it against a whitelist first.
<< End of Update 1
You should read the MySQL Full-Text Search Functions, and the String Comparison Functions. You need to learn how to construct basic SQL statements, or else writing even a simple search engine will prove extremely difficult.
There are plenty of PDO examples on the PHP site too. You could start with the documentation for PDOStatement->execute, which contains some examples of how to use the function.
If you have access to the MySQL CLI, or even PHPMyAdmin, you can try out your SQL without all the PHP confusing things. If you are going to be doing any database development work as part of your PHP application, you will find being able to test SQL independently of PHP a great help.