Ok, i have a problem here...
I am sending values of drop down lists via ajax to this PHP file.
Now I want to search a mysql database using these values, which I have managed to do, BUT, only if I set the values to something...
Take a look:
$query = "SELECT * FROM cars_db WHERE price BETWEEN '$cars_price_from' AND '$cars_price_to' AND year BETWEEN '$cars_year_from' AND '$cars_year_to' AND mileage BETWEEN '$cars_mileage_from' AND '$cars_mileage_to' AND gearbox = '$cars_gearbox' AND fuel = '$cars_fuel'";
now, what if the user doesnt select any "price_from" or "year_from"... The fields are only optional, so if the user doesnt enter any "price from" or "year from", then the user wants ALL cars to show...
Do I have to write a query statement for each case or is there another way?
I do something similar to davethegr8 except I put my conditions in an array and then implode at the end just so I don't have to worry about which conditions got added and whether I need to add extra AND's.
For example:
$sql = "SELECT * FROM car_db";
// an array to hold the conditions
$conditions = array();
// for price
if ($car_price_from > 0 && $car_price_to > $car_price_from) {
$conditions[] = "(price BETWEEN '$cars_price_from' AND '$cars_price_to')";
}
elseif ($car_price_from > 0) {
$conditions[] = "(price >= '$cars_price_from')";
}
elseif ($car_price_to > 0) {
$conditions[] = "(price <= '$cars_price_from')";
}
else {
//nothing
}
// similar for the other variables, building up the $conditions array.
// now append to the existing $sql
if (count($conditions) > 0){
$sql .= 'WHERE ' . implode(' AND ', $conditions);
}
You could simply detect which parameters are missing in your PHP code and fill in a suitable default. eg
if (!isset($cars_mileage_to))
$cars_mileage_to = 500000;
You can build you query, adding the "where" part only if your variables are different from "".
or if you're using mysql 5.x, you can also use subselects:
http://dev.mysql.com/doc/refman/5.0/en/subqueries.html
don't forget to validate the input. It's trivial with firebug, for example, to inject some tasty sql.
Related
I have a web application and I'm trying to modify one of the queries. The query fetches information (from a table named voyage_list) and returns various fields.
I want to modify the query so that it is based on certain filters the user applies (which will be placed in the URL).
I can't get the query to work in the web application, but if I copy the query and execute it directly within PHPMyAdmin, it works fine.
$vesselFilter = $_GET['vesselFilter'];
$vesselArray = explode(',', $vesselFilter);
$arrayCount = count($vesselArray);
$sqlExtend = ' status = 1 AND';
foreach ($vesselArray as $value) {
$i = $i + 1;
$sqlExtend .= " vesselID = '$value'";
if ($i < $arrayCount){
$sqlExtend .= " OR";
}
}
$newQuery = "SELECT * FROM voyage_list WHERE" . $sqlExtend;
echo $newQuery;
$query = $db->query($newQuery)->fetchAll();
I appreciate the above is pretty messy, but it's just so I can try and figure out how to get the query to work.
Any help would be greatly appreciated!
Thanks
That query probably doesn't return what you think it does. AND takes precedence over OR, so it will return the first vessel in the list if the status is 1, and also any other vessel in the list, regardless of status.
You'd do better to create a query with an IN clause like this:
SELECT * FROM voyage_list WHERE status = 1 AND vesselID IN(8,9,10)
Here's some code to do just that:
$vesselFilter = $_GET['vesselFilter'];
// Validate data. Since we're expecting a string containing only integers and commas, reject anything else
// This throws out bad data and also protects against SQL injection.
if (preg_match('/[^0-9,]/', $vesselFilter)) {
echo "Bad data in input";
exit;
}
// filter out any empty entries.
$vesselArray = array_filter(explode(',', $vesselFilter));
// Now create the WHERE clause using IN
$sqlExtend = 'status = 1 AND vesselID IN ('.join(',', $vesselArray).')';
$newQuery = "SELECT * FROM voyage_list WHERE " . $sqlExtend;
echo $newQuery;
$query = $db->query($newQuery)->fetchAll();
var_dump($query);
I have made a small intranet website to collect and store data to be used to expedite our logistics processes. I'm now in the process of adding search functionality which, if records are found that match that criteria, will allow the user to quickly select parts of that data to pre-populate a new shipping request with data (e.g, the user types 'Mar' in the Recipient Name input textbox and '109' in the Street Address input textbox and the query returns two records: {"Mary Smith", "1090 South Central St"} and {"Mark Swanson", "109 E. 31st St."}).
At the moment, when search criteria is entered and submitted, the data returned from the query in PHP is 100% accurate if and only if a single criteria is entered (such as Recipient Name). When I attempt to use two different search criterias in PHP, the record results do not match the results when running the same query in Oracle PL/SQL Developer. If three different search criterias are used, the query ran in PHP will return 0 records. In all three of the aforementioned scenarios, the query is executed without error in Oracle PL/SQL Developer.
The following code is from my PHP search function. The input data to this function is an associate array of field names and the user inputted search criteria data for that field.
public function Search()
{
if($this->dbcon)
{
$query = "SELECT * FROM ship_request ";
$postCount = count($this->post_data);
$counter = 0;
if ($postCount > 0)
{
$query .= "WHERE ";
}
foreach ($this->post_data as $k => $v)
{
$counter++;
if (strlen($v) > 0)
{
if ($k == 'SR_DATE')
{
$query .= $k . " = :" . $k . " AND ";
} else {
$query .= "upper(" . $k . ") like upper(:" . $k . ") AND ";
}
}
}
if (substr($query,-4) == "AND ")
{
$query = substr($query, 0, strlen($query) - 4);
}
$stid = oci_parse($this->ifsdb, $query);
foreach ($this->post_data as $k => $v)
{
if (strlen($v) > 0)
{
if ($k == 'SR_DATE')
{
$this->post_data[$k] = date("d-M-y", strtotime($this->post_data[$k]));
$placeHolder = $this->post_data[$k];
} else {
$placeHolder = '%' . $this->post_data[$k] . '%';
}
oci_bind_by_name($stid, $k, $placeHolder);
}
}
oci_execute($stid);
$nrows = oci_fetch_all($stid, $recordsFound);
$recordsFound = json_encode($recordsFound);
oci_free_statement($stid);
echo $recordsFound;
} else {
die("Could not connect to database!");
}
}
}
I've done a var_dump on $query to see what my query actually looks like when I enter multiple search criteria values. This is an example of what I see:
select * from HOL_SHIP_REQUEST where upper(sr_shipper_name) like upper(:sr_shipper_name) and upper(sr_recipient_name) like upper(:sr_recipient_name) and sr_recipient_phone like upper(:sr_recipient_phone)
That query returns 0 records when I enter "a" for Shipper Name, "m" for Recipient Name, and "2" for Phone Number.
This query, when executed in Oracle PL/SQL Developer, however, returns 27 records.
select * from HOL_SHIP_REQUEST where upper(sr_shipper_name) like upper('%a%') and upper(sr_recipient_name) like upper('%m%') and sr_recipient_phone like upper('%2%')
Is there something wrong with the way that I'm trying to bind the parameters in PHP? Is there something different I have to do when using multiple like statements?
You've forgotten the % wildcard chars in your built query string. The DB interface libraries do NOT parse the query you're building, and do NOT look for LIKE clauses - it's not their job to guess what kind of match you're trying to do. e.g. are you doing
WHERE a LIKE 'b'
WHERE a LIKE 'b%'
WHERE a LIKE '%b'
WHERE a LIKE '%b%'
It's up to you to provide the appropriate wildcards, and since you're using placeholders, you'll have to do it yourself, e.g.
WHERE UPPER(sr_shipper_name) LIKE CONCAT('%', :sr_shipper_name, '%')
If you were to do it something like this:
$shipper = '%foo%';
WHERE ... LIKE :shipper
you'd end up with the equivalent of:
WHERE ... LIKE '\%foo\%'
The placeholder system also doesn't parse your provided text and try to figure out if you're really trying to use a wilcard or just passing in a literal % char. That's why you have to use the CONCAT hack to build a proper wildcarded construct.
I have a search form with a possible 15 or so fields, however not all are required to carry out a search, for instance;
a user might search for a registered user in 'London' who works in 'Finance' but leave all other fields blank, such as $availability or $salary etc, so $_POST data may look something like:
$location = $_POST['location']; // Value - London
$sector = $_POST['sector']; // Value - Finance
$available = $_POST['available']; // Value - Any
$salary = $_POST['salary']; // Value - Any
Bearing in mind I may have another 12 or so 'Any' values from other fields, what is the best way to query the database (PHP/MySQL) to return results without looping through what would probably be dozens of queries.
To try and be a bit clearer, what i'd like is a query which would work something like (deliberate pseudo code):
SELECT * FROM table where location = 'location' AND if($availability !='Any') { available = '$available' } etc etc
Is something like this possible?
Or can I create a single string of all $_POST fields that !='Any' and then carry out a search on a row that contains all the words in the string (which I think would work in theory)?
I hope this makes sense to someone and you can point me in the right direction.
P.S. All $_POST is escaped and secured before interacting with database, just not included here :)
Try this:
$sql = "SELECT * FROM table where 1 ";
foreach ($_POST as $key => $post) {
if ($post != 'Any') {
$sql .= " AND $key = '$post' ";
}
}
// now you can run $sql against the database
Could you for argument sake collect all of the $_POST into a foreach($key=>$val) and then run the key through a switch or if statments that appends "AND x=x " to the statement?
Something like:
$sql = "SELECT * FROM table WHERE required='required'";
foreach($_POST as $key=>$val){
if(!empty($val)){ $sql .= " AND ".$key."='".$val"'"; }
}
Not sure if that works but in theory that is what i thought of first.
Thanks to those who offered answers, however I used the suggested answer found in the link above my question as it was clearer to me. Sample code pasted below FYI:
$tmp = "where ";
if($A and $A!="any" and $A!="not used")
$tmp .= "row1 = '".$A."'";
if($B and $B!="any" and $B!="not used")
$tmp .= "AND row2 = '".$B. "'";
if($C and $C!="any" and $C!="not used")
$tmp .= "AND row3 = '".$C."'";
$db_q = "Select * from table $tmp";
Thanks again, don't know where I'd be without SO.
I have some search functionality that works with 3 drop down boxes. Based on the criteria chosen, a profile is returned. The 3 drop downs are:
County
Constituency
Gender
Now I am trying to build a query but have just realised that actually a person does not have to choose an option from each drop down and nor do I want them to.
So for instance I do not want to disable the search button until an option is selected from each drop down.
Having chosen a value from any drop down, and possibly having no value selected from any drop down at all, and just clicking the search button, I am trying to understand how I can cope with the unknown combinations.
My first thought was that I could use something like a truth table but I imagine this is simply overkill and in fact this is a very common piece of functionality.
Then I thought maybe I could have something like:
$county = "$_GET['county'];";
$constituency = "$_GET['constituency'];";
$gender = "$_GET['gender'];";
Then I could check to see if they are empty and somehow use this value, e.g.
if($county !== '') {
???SOMEHOW MAKE USE OF THIS IN AN SQL QUERY???
PERHAPS PASS IT TO ANOTHER PARAMETER
$sqlparams = "county = '$county'";
}
SELECT * FROM profile
WHERE {$sqlparams};
I think I'm on the right tracks but could use some guidance.
All help is greatly appreciated.
This should do want you want, I think.
<?php
$tooLookFor = array("county", "constituency", "gender");
foreach($tooLookFor as $key){
if(isset($_GET[$key]) && strlen($_GET[$key])>0){
$queryParams[] = $key.'="'.$_GET[$key].'"';
}
}
$query = "SELECT * FROM profile WHERE ".implode(' AND ', $queryParams);
?>
You could do something like:
$county = $_GET['county'];
$constituency = $_GET['constituency'];
$gender = $_GET['gender'];
$sqlparams = array();
if($county !== '') {
$sqlparams[] = "county = '$county'";
}
if($constituency !== '') {
$sqlparams[] = "constituency = '$constituency'";
}
if($gender !== '') {
$sqlparams[] = "gender = '$gender'";
}
$query = "SELECT * FROM profile";
if (count($sqlparams) > 0) {
$query .= " WHERE " . implode(" AND ", $sqlparams);
}
You can do that with something like this:
$where = array();
//repeat as needed
$where[$column] = $value;
$where2 = array();
foreach($where as $key => $value){
$where2[] = "$key = '$value'";
}
$where_string = implode(' AND ', $where2);
$where_string will have the string to insert after WHERE.
Yes, you are on the right track, you're just not at the right switch yet. ;)
You can't build the query until you know what you have to work with. So first, in your validation, determine (as you are doing) with the key words actually are and what fields they represent. Presumably these map to fields in tables, maybe 3 tables? Point is, your query will need to be dynamically built.
I'm trying to create an Advanced Searching form that sort of look like this ;
http://img805.imageshack.us/img805/7162/30989114.jpg
but what should I write for the query?
I know how to do it if there is only two text box but three, there's too many probability that user will do.
$query = "SELECT * FROM server WHERE ???";
What should I write for the "???"
I know how to use AND OR in the query but lets say if the user only fill two of the textbox and one empty. If I write something like this ;
$query = "SELECT * FROM server WHERE model='".$model."' and brand='".$brand."' and SN='".$SN.'" ";
The result will return as empty set. I want the user can choose whether to fill one,two or three of the criteria. If I use OR, the result will not be accurate because if Model have two data with the same name (For example :M4000) but different brand (For example : IBM and SUN). If I use OR and the user wants to search M4000 and SUN, it will display both of the M4000. That's why it is not accurate.
If the user can decide how many criteria he wants to enter for your search and you want to combine those criteria (only those actually filled by the user), then you must dynamically create your SQL query to include only those fields in the search that are filled by the user. I'll give you an example.
The code for a simple search form could look like this:
$search_fields = Array(
// field name => label
'model' => 'Model',
'serialNum' => 'Serial Number',
'brand' => 'Brand Name'
);
echo "<form method=\"POST\">";
foreach ($search_fields as $field => $label) {
echo "$label: <input name=\"search[$field]\"><br>";
}
echo "<input type=\"submit\">";
echo "</form>";
And the code for an actual search like this:
if (isset($_POST['search']) && is_array($_POST['search'])) {
// escape against SQL injection
$search = array_filter($_POST['search'], 'mysql_real_escape_string');
// build SQL
$search_parts = array();
foreach ($search as $field => $value) {
if ($value) {
$search_parts[] = "$field LIKE '%$value%'";
}
}
$sql = "SELECT * FROM table WHERE " . implode(' AND ', $search_parts);
// do query here
}
else {
echo "please enter some search criteria!";
}
In the above code we dynamically build the SQL string to do a search ("AND") for only the criteria entered.
Try this code
<?php
$model="";
$brand="";
$serialNum="";
$model=mysql_real_escape_string($_POST['model']);
$brand=mysql_real_escape_string($_POST['brand']);
$serialNum=mysql_real_escape_string($_POST['serialNum']);
$query=" select * from server";
$where_str=" where ";
if($model == "" && $brand == "" && $serialNum == "")
{
rtrim($where_str, " whrere ");
}
else
{
if($model != "")
{
$where_str.= " model like '%$model%' AND ";
}
if($brand != "")
{
$where_str.= " brand like '%$brand%' AND ";
}
if($serialNum != "")
{
$where_str.= " serialNum like '%$serialNum%' AND ";
}
rtrim($where_str, " AND ");
}
$query.= $where_str;
$records=mysql_query($query);
?>
For those framiliar with mysql, it offers the ability to search by regular expressions (posix style). I needed an advanced way of searching in php, and my backend was mysql, so this was the logical choice. Problem is, how do I build a whole mysql query based on the input? Here's the type of queries I wanted to be able to process:
exact word matches
sub-string matches (I was doing this with like "%WORD%")
exclude via sub-string match
exclude via exact word match
A simple regexp query looks like:
select * from TABLE where ROW regexp '[[:<:]]bla[[:>:]]' and ROW
regexp 'foo';
This will look for an exact match of the string "bla", meaning not as a sub-string, and then match the sub-string "foo" somewhere.
So first off, items 1 and 4 are exact word matches and I want to be able to do this by surrounding the word with quotes. Let's set our necessary variables and then do a match on quotes:
$newq = $query; # $query is the raw query string
$qlevel = 0;
$curquery = "select * from TABLE where "; # the beginning of the query
$doneg = 0;
preg_match_all("/\"([^\"]*)\"/i", $query, $m);
$c = count($m[0]);
for ($i = 0; $i < $c; $i++) {
$temp = $m[1][$i]; # $temp is whats inside the quotes
Then I want to be able to exclude words, and the user should be able to do this by starting the word with a dash (-), and for exact word matches this has to be inside the quotes. The second match is to get rid of the - in front of the query.
if (ereg("^-", $temp)) {
$pc = preg_match("/-([^-]*)/i", $m[1][$i], $dm);
if ($pc) {
$temp = $dm[1];
}
$doneg++;
}
Now we will set $temp to the posix compliant exact match, then build this part of the mysql query.
$temp = "[[:<:]]".$temp."[[:>:]]";
if ($qlevel) $curquery .= "and "; # are we nested?
$curquery .= "ROW "; # the mysql row we are searching in
if ($doneg) $curquery .= "not "; # if dash in front, do not
$curquery .= "regexp ".quote_smart($temp)." ";
$qlevel++;
$doneg = 0;
$newq = ereg_replace($m[0][$i], "", $newq);
}
The variable $newq has the rest of the search string, minus everything in quotes, so whatever remains are sub-string search items falling under 2 and 3. Now we can go through what is left and basically do the same thing as above.
$s = preg_split("/\s+/", $newq, -1, PREG_SPLIT_NO_EMPTY); #whitespaces
for ($i = 0; $i < count($s); $i++) {
if (ereg("^-", $s[$i])) { # exclude
sscanf($s[$i], "-%s", $temp); # this is poor
$s[$i] = $temp;
$doneg++;
}
if ($qlevel) $curquery .= "and ";
$curquery .= "ROW "; # the mysql row we are searching in
if ($doneg) $curquery .= "not ";
$curquery .= "regexp ".quote_smart($s[$i])." ";
$qlevel++;
$doneg = 0;
}
# use $curquery here in database
The variable $curquery now contains our built mysql query. You will notice the use of quote_smart in here, this is a mysql best practice from php.net. It's the only mention of security anywhere in this code. You will need to run your own checking against the input to make sure there are no bad characters, mine only allows alpha-numerics and a few others. DO NOT use this code as is without first fixing that.
You have to provide $model, $brand, $serial which come from your search-form.
$query = "SELECT * FROM `TABLE` WHERE `model` LIKE '%$model%' AND `brand` LIKE '%$brand%' AND `serial` LIKE '%$serial%'";
Also take a look at the mysql doc
http://dev.mysql.com/doc/refman/5.1/en/string-comparison-functions.html
A basic search would work like this:
"SELECT * FROM server WHERE column_name1 LIKE '%keyword1%' AND column_name2 LIKE '%keyword2%' .....";
This would be case for matching all parameters.For matching any one of the criteria, change ANDs to ORs