I'm using php to compile a long mySQL query. For the sake of context, I'm making a mySQL query that scans through the table 'rentals', which has the fields 'id', 'rent', 'type', 'num_bedrooms'. Example of an entry could be:
rent=600, type=house, num_bedrooms=5
or
rent=450, type=apartment, num_bedrooms=3
When filling out the search field, the user has the option to search 'any' for 'type' and 'num_bedrooms'. Paying attention to the line of SQL 'type IS NOT NULL': **Is it possible to say 'type = NOT NULL' instead?
(the idea I have is that if the user searches 'any' for 'type', then I set a variable '$type' in php as either 'house' to find houses, 'apartment' to find apartments, or 'NOT NULL' to find all)
<?php
$min_rent = 200;
$max_rent = 1000;
$type = "NOT NULL"; //or it can be equal to "house" or "apartment"
$num_bedrooms = 2; //e.g. 2 or more bedrooms
$sql = "SELECT * FROM properties WHERE ";
$sql .= "rent >= {$min_rent} AND rent <= {$max_rent} AND ";
$sql .= "type = {$type} "; //in this case, I want 'type' to equal 'NOT NULL'
$sql .= "num_bedrooms >= {$num_bedrooms} ";
?>
Actually there is an alternative to is not NULL. For example:
SELECT * FROM properties WHERE type = type
This will return all rows when type is not null.
No - IS NOT NULL is a special function.
Generally what you would do is put the
$sql .= "type = {$type} ";
into an if statement, that would check the value of $type as the SQL statement was constructed.
Welcome to SO.
SQL treats a null differently than another string. It's not possible to say type = "NOT NULL". MySQL will return the types with that contain NOT NULL. (Not, as you might hope, those containing "house" or "apartment."
That line would have to read $sql .= "type IS NOT NULL "
Of course, you could put an IF statement that appends the above line in your special case.
One option is:
SELECT * FROM properties WHERE type != ''
Related
I need to change this query to use a prepared statement. Is it possible?
The query:
$sql = "SELECT id, title, content, priority, date, delivery FROM tasks " . $op . " " . $title . " " . $content . " " . $priority . " " . $date . " " . $delivery . " ORDER BY " . $orderField . " " . $order . " " . $pagination . "";
Before the query, there's code to check the POST variables and change the content of variables in the query.
//For $op makes an INNER JOIN with or without IN clause depending on the content of a $_POST variable
$op = "INNER JOIN ... WHERE opID IN ('"$.opID."')";
//Or
$op = "INNER JOIN ... ";
//For $title (depends of $op):
$title = "WHERE title LIKE'%".$_POST["title"]."%'";
//Or
$title = "AND title LIKE'%".$_POST["title"]."%'";
//For $content:
$content = "AND content LIKE '%".$_POST["content"]."%'";
//For $priority just a switch:
$priority = "AND priority = DEPENDING_CASE";
//For $date and $delivery another switch
$d = date("Y-m-d", strtotime($_POST["date"]));
$date = "AND date >= '$d' 00:00:00 AND date <= '$d' 23:59:59";
//Or $date = "AND date >= '$d' 00:00:00";
//Or $date = "AND date <= '$d' 23:59:59";
//For $orderField
$orderField = $_POST["column"];
//For $order
$order= $_POST["order"];
//For $pagination
$pagination = "LIMIT ".$offset.",". $recordsPerPage;
How I could do this query using prepared statement?
The query could be more static but this means to make different prepared statements and execute it depending of $_POST checks.
It depends on many variables because this query show results in a table that contains search fields and column to order.
A full example of query would be like this (depending of $_POST checks):
SELECT id, title, content, priority, date, delivery FROM tasks INNER JOIN op ON task.op = op.opId WHERE op IN (4851,8965,78562) AND title LIKE '%PHT%' AND content LIKE '%%' AND priority = '2' ORDER BY date DESC LIMIT 0, 10
An excellent question. And thank you for moving to prepared statements. It seems that after all those years of struggle, the idea finally is starting to take over.
Disclaimer: there will be links to my own site because I am helping people with PHP for 20+ years and got an obsession with writing articles about most common issues.
Yes, it's perfectly possible. Check out my article, How to create a search filter for mysqli for the fully functional example.
For the WHERE part, all you need is to create two separate arrays - one containing query conditions with placeholders and one containing actual values for these placeholders, i.e:
WHERE clause
$conditions = [];
$parameters = [];
if (!empty($_POST["content"])) {
$conditions[] = 'content LIKE ?';
$parameters[] = '%'.$_POST['content ']."%";
}
and so on, for all search conditions.
Then you could implode all the conditions using AND string as a glue, and get a first-class WHERE clause:
if ($conditions)
{
$where .= " WHERE ".implode(" AND ", $conditions);
}
The routine is the same for all search conditions, but it will be a bit different for the IN() clause.
IN() clause
is a bit different as you will need more placeholders and more values to be added:
if (!empty($_POST["opID"])) {
$in = str_repeat('?,', count($array) - 1) . '?';
$conditions[] = "opID IN ($in)";
$parameters = array_merge($parameters, $_POST["opID"]);
}
this code will add as many ? placeholders to the IN() clause as many elements in the $_POST["opID"] and will add all those values to the $parameters array. The explanation can be found in the adjacent article in the same section on my site.
After you are done with WHERE clause, you can move to the rest of your query
ORDER BY clause
You cannot parameterize the order by clause, because field names and SQL keywords cannot be represented by a placeholder. And to tackle with this problem I beg you to use a whitelisting function I wrote for this exact purpose. With it you can make your ORDER BY clause 100% safe but perfectly flexible. All you need is to predefine an array with field names allowed in the order by clause:
$sortColumns = ["title","content","priority"]; // add your own
and then get safe values using this handy function:
$orderField = white_list($_POST["column"], $sortColumns, "Invalid column name");
$order = white_list($_POST["order"], ["ASC","DESC"], "Invalid ORDER BY direction");
this is a smart function, that covers three different scenarios
in case no values were provided (i.e. $_POST["column"] is empty) the first value from the white list will be used, so it serves as a default value
in case a correct value provided, it will be used in the query
in case an incorrect value is provided, then an error will be thrown.
LIMIT clause
LIMIT values are perfectly parameterized so you can just add them to the $parameters array:
$limit = "LIMIT ?, ?";
$parameters[] = $offset;
$parameters[] = $recordsPerPage;
The final assembly
In the end, your query will be something like this
$sql = "SELECT id, title, content, priority, date, delivery
FROM tasks INNER JOIN ... $where ORDER BY `$orderField` $order $limit";
And it can be executed using the following code
$stmt = $mysqli->prepare($sql);
$stmt->bind_param(str_repeat("s", count($parameters)), ...$parameters);
$stmt->execute();
$data = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
where $data is a conventional array contains all the rows returned by the query.
I have a form that POST value keeps coming as "" empty string into a unique SQL field.
It has to be unique as the field is optional but jet it can not have 2 same values. The unique value does allow null multiple values.
I don't even want to say what I tried to do, I'm trying to fix this for last few days.
Closest that I got is putting '$OIB'=IF('$OIB'='',NULL,'$OIB' into NSERT INTO statement, then i get null values into a database but for some reason when the number is entered into OIB form field it puts number 1 into a database...
$result = mysqli_query($conn, "SELECT OIB, NAZIV FROM STRANKEP WHERE OIB = '$OIB' OR NAZIV = '$NAZIV'");
if(mysqli_num_rows($result) == 0) {
// row not found, do stuff...
$sql = "INSERT INTO STRANKEP (NAZIV, OIB, EMAIL, ADRESA, TELEFON) VALUES ('$NAZIV', CASE WHEN '$OIB' = '' THEN 'NULL', '$EMAIL', '$ADRESA', '$TELEFON')";
$query = mysqli_query($conn, $sql);
This solution gets me null but not the real $OIB when entered into form, it just puts number 1.
$result = mysqli_query($conn, "SELECT OIB, NAZIV FROM STRANKEP WHERE OIB = '$OIB' OR NAZIV = '$NAZIV'");
if(mysqli_num_rows($result) == 0) {
// row not found, do stuff...
$sql = "INSERT INTO STRANKEP (NAZIV, OIB, EMAIL, ADRESA, TELEFON) VALUES ('$NAZIV', '$OIB'=IF('$OIB'='',NULL,'$OIB'), '$EMAIL', '$ADRESA', '$TELEFON')";
$query = mysqli_query($conn, $sql);
Thank you in advance for the help.
Try
CASE '$OIB' WHEN '' THEN NULL ELSE '$OIB' END
You can use also IF Clause
Like so
INSERT INTO STRANKEP (NAZIV, OIB, EMAIL, ADRESA, TELEFON) VALUES
('$NAZIV', IF('$OIB' = '', NULL,'$OIB'), '$EMAIL', '$ADRESA', '$TELEFON');
But as mentioned in my comment use prepared statements like in PDO https://phpdelusions.net/pdo#prepared
I would recommend nullif(). It is a built-in standard function to do exactly this:
nullif(?, '')
Note: Do not munge queries with constant values. That makes the code subject to SQL injection attacks. And it can introduce very hard-to-debug errors. Use parameters!
I have a perl DBI mysql query that looks like this:
my $rows = get_rows(
"select id from table where column1=?, column2=? and column3=?",
$var1,$var2,$var3
);
sub get_rows {
my $sql = shift;
my #vars = #_;
my $sth = $dbh->prepare($sql);
$sth->execute(#vars);
my $rows = $sth->fetchall_arrayref();
$sth->finish();
return $rows;
}
I'm running this to check if a particular row exists containing those vars. The problem I have however is with dealing with NULL values. These have to be selected as IS NULL rather than =?, which very often then misses rows that contain NULL values. For example, if vars 1, 2 and 3 contain '12', '23' and undef and table contains 12, 23 and NULL then the query returns no results. Is there a simple way to transform undef values into IS NULL values with DBI?
This is documented in DBI under NULL Values.
$sql_clause = defined $age? "age = ?" : "age IS NULL";
$sth = $dbh->prepare(qq{
SELECT fullname FROM people WHERE $sql_clause
});
$sth->execute(defined $age ? $age : ());
I have a PHP file which is taking in seven variables like so:
$name=$_REQUEST['membername'];
$email=$_REQUEST['email'];
$dob=$_REQUEST['dob'];
$gender=$_REQUEST['gender'];
$phone=$_REQUEST['phone'];
$county=$_REQUEST['county'];
$IP=$_REQUEST['IP'];
Some of these will not be set. What I want to do is construct a query which will search the members table such that if only $email and $dob are set it will only search by $email and $dob, ignoring the others. Or if only $phone, $name, and $gender are set, it will search those three columns only.
Is there an easier method than constructing a big block of if isset functions covering all possible permutations?
If you don't want to search on a field, pass NULL for the parameter and structure your WHERE clause something like...
WHERE
( (#parameter1 IS NULL) OR (column1 = #parameter1) )
AND
( (#parameter2 IS NULL) OR (column2 = #parameter2) )
I don't spend much time in MYSQL so the syntax is probably a bit off but you get the idea.
Presuming that you use parameters to push values into the query...
SELECT *
FROM MyTable
WHERE name = COALESCE(#p1, name)
OR email = COALESCE(#p2, email)
OR dob = COALESCE(#p3, dob)
...
...
If you construct a query string in PHP you can, instead, take another tack:
function AddWhere(&$where, $dbFieldName, $fieldValue)
{
if ($fieldValue <> "")
{
if (strlen($fieldName) > 0)
$fieldName .= " AND ";
$fieldname .= '(' + $dbFieldName + ' = \'' + $fieldValue + '\')'
}
}
Then, when you're retrived the variables, build a SQL statement thusly
$whereClause = ''
AddWhere($whereClause, 'name', $name)
AddWhere($whereClause, 'email', $email)
AddWhere($whereClause, 'dob', $dob)
...
IF (strlen($whereClause) > 0)
{
$sql = 'SELECT * FROM MyTable WHERE ' + $whereClause
... etc
}
(I'm not great at PHP, so the syntax may be somewhat screwed up).
I am having an issue with inserting an array of information into a mysql database. Basically I built a sortable gallery similar to Facebook's photo albums that can be arranged by moving the div to a new spot with jquery's sortable function.
I am using Ajax to call a php file which will inser the new order of the div's into the DB. The information is being passed correctly, it is just not being inserted correctly.
The error I am receiving is:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Array' at line 1
The Php code is:
foreach ($_GET['listItem'] as $position => $item) {
if ($item >= 1) {
$sql[] = "UPDATE table SET order = '{$position}' WHERE id = '{$item}'";
mysql_query($sql) or die(mysql_error());
}
}
If I remove the mysql_query function and just do a print_r, I get:
Array
(
[0] => UPDATE table SET order = '0' WHERE id = '2'
[1] => UPDATE table SET order = '1' WHERE id = '4'
[2] => UPDATE table SET order = '2' WHERE id = '3'
[3] => UPDATE table SET order = '3' WHERE id = '1'
[4] => UPDATE table SET order = '4' WHERE id = '5'
[5] => UPDATE table SET order = '5' WHERE id = '6'
)
This is the first time I have tried to do something like this. Any help would be great.
Thank you in advance for the help!
In mysql_query($sql) $sql is an array, therefore it's value is simply Array. When you assign $sql[] = "UPDATE table SET order = '{$position}' WHERE id = '{$item}'"; simply make this line $sql = "UPDATE table SET order = '{$position}' WHERE id = '{$item}'";. That should solve your problem.
EDIT:
You can leave the [] and simply remove the mysql_query from where it is. After your foreach list item, add this:
foreach($sql as $query) {
mysql_query($query);
}
Sounds like there is some confusion about what the [] operator does. You use [] when you want to append an element to the end of an existing array.
For example:
$sql = array();
$sql[] = 'UPDATE table SET order = "0" WHERE id = "2"';
mysql_query($sql); // this will produce the error you are seeing
Versus:
$sql = 'UPDATE table SET order = "0" WHERE id = "2"';
mysql_query($sql); // this will work
You should rewrite your code as such:
foreach ($_GET['listItem'] as $position => $item) {
if ($item >= 1) {
$sql = "UPDATE table SET order = '{$position}' WHERE id = '{$item}'";
mysql_query($sql) or die(mysql_error());
}
}
That will do what you are intending. However, this is still not a good idea, since you are passing untrusted $_GET data directly to the database. I could, for example, call your script with a string like:
http://yoursite.com/yourscript.php?listItem=1'%3B%20DROP%20TABLE%20yourtable%3B
Since the value of listItem is going directly to the database -- and the $item >= 1 check is insufficient, since PHP will evaluate a string as an integer if it begins with numeric data -- all I have to do is add a single quote to terminate the previous query, and I am then free to inject whatever SQL command I'd like; this is a basic SQL injection attack. Whenever you write database-touching code, you should cleanse any input that might be going to the database. A final version of your code might look like:
foreach ($_GET['listItem'] as $position => $item) {
if ($item >= 1) { // this check may or may not be needed depending on its purpose
$sql = 'UPDATE table SET order = "' . mysql_real_escape_string($position) . '" WHERE id = "' . mysql_real_escape_string($item) . '"';
mysql_query($sql) or die(mysql_error());
}
}
There are other ways to cleanse input data as well, that is just one of them. Hope that helps.