MySQL Use of IN, LIKE and OR - php

I am attempting to write a search algorithm, nothing too advanced but it isnt just WHERE field1 = 'searchtext'. I am trying to search all keywords across multiple fields.
I have done a bit of searching and it seems as though my take on the matter is not compliant with MySQL's use of 'IN' with other functions, however I cannot find anything that seems to suggest a better way either here on stackoverflow or using google on independant blogs and other tutorial sites.
$fields = array('type','suburb','postcode','address'); // Fields in db being searched
$queried = $db->real_escape_string($_REQUEST['keyword']); // Input from form
$keys = explode(" ",$queried); // Determine individual keywords
$sql = "SELECT * FROM `properties` WHERE "; // Beginning of SQL Statement
$frc = 0; // Field Counter
foreach($fields as $f){
$inner = ''; // Reset $inner each run
$irc = 0; // Reset Inner Counter each run
$frc++; // Increase Field Counter
if($frc != 1){ $sql .= " OR "; } // All except first runthrough
$sql .= "`".$f."` IN "; // `field` IN
foreach($keys as $k){
$irc++; // Increase inner counter
if($irc == 1){
$inner .= "('%".$k."%'"; // First Inner per run (aka each keyword)
}else{
$inner .= ", '%".$k."%'"; // All other Inners
}
}
$inner .= ")"; // Inner finishes run before reset
$sql .= $inner; // Add Inner to SQL ready for query
}
$sql .= ";"; // Clean finish to SQL statement
$SearchProperties = $db->query($sql); // Run Query
I have included commentary to help you understand my messy code and what I felt I was doing. The code is giving me the expected output, for example if I search the keyword "house" my output is as follows;
$queried = house 3064
$sql = SELECT * FROM `properties` WHERE `type` IN ('%house%', '%3064%') OR `suburb` IN ('%house%', '%3064%') OR `postcode` IN ('%house%', '%3064%') OR `address` IN ('%house%', '%3064%');
Within the type column there is house and townhouse, it should be able to hit both, and should hit anything with the postcode 3064 regardless of if it has house in another column (According to what I want to accomplish)
However after several hours of searching, although my output is as desired I don't believe it to be correct. Could anybody help shed some light on the CORRECT method of solving my quandry and WHY this does not work? I always like to understand and learn from these sort of misunderstandings.
Thank you for your help.

If you have wildcards, you want like rather than in:
SELECT *
FROM `properties`
WHERE (`type` LIKE '%house%') OR
(`suburb` LIKE '%house%') OR
(`postcode` LIKE '%house%') OR
(`address` LIKE '%house%');
However, I would strongly suggest that you investigate full text indexes (see here). The use of MATCH() may greatly simplify your efforts.
EDIT:
Your query is still incorrect. And you should still be using like:
SELECT *
FROM `properties`
WHERE (`type` LIKE '%house%' or type like '%3064%') OR
(`suburb` LIKE '%house%' or suburb like '%3064%') OR
(`postcode` LIKE '%house%' or postcode like '%3064%') OR
(`address` LIKE '%house%' or address like '%3064%');

Try change 'IN' to 'LIKE'.
For example
$queried = house
$sql = SELECT * FROM `properties` WHERE
`type` LIKE '%house%'
OR `suburb` LIKE '%house%'
OR `postcode` LIKE '%house%'
OR `address` LIKE '%house%';
If you have a several keywords, then you need change query.
For example
$queried = house 3064
$sql = SELECT * FROM `properties` WHERE
(`type` LIKE '%house%' AND `type` LIKE '%3064%')
OR (`suburb` LIKE '%house%' AND `suburb` LIKE '%3064%')
OR (`postcode` LIKE '%house%' AND `postcode` LIKE '%3064%')
OR (`address` LIKE '%house%' AND `address` LIKE '%3064%');

Related

Make the user only able to see documents uploaded by themselves

Good afternoon all,
I'm in the finishing stages of making a website where users can register and login. After that they can upload documents such as .pdf and .docx and search for those documents. The users data gets stored in my users table which contains following:
idUsers int(11) PRIMARY KEY NOT NULL,
uidUsers TINYTEXT NOT NULL,
emailUsers TINYTEXT NOT NULL,
pwdUsers LONGTEXT NOT NULL,
Then I have a table called "files" where the files that are being uploaded by the user i stored. The table contains following:
id int(11) PRIMARY KEY NOT NULL,
usersId int(11) NOT NULL, - (Foreign key for idUsers in users table)
name varchar(255) NOT NULL,
title varchar(255) NOT NULL,
forfatter varchar(255) NOT NULL, (forfatter = author)
size int(11),
download int(11) (for later use)
So when a specific user are logged in and uploads document, the users "id" (primary key in users table) gets passed to "usersId" in my files table.
All of this is working perfectly fine so far.
Then the user are able to use a search function, where the user can search for name, title and author of the document, which also works fine.
I only need to make sure the user only can see the documents, that are being uploaded by themselves. But I can't figure it out. I've tried many different solutions so far.
I've tried a suggestion i saw, where i tried to make my mysqli_query look like this:
"
SELECT *
FROM files
WHERE id
AND usersId name LIKE '%$searchingq%'
OR title LIKE '%$searchingq%'
OR forfatter LIKE '%$searchingq%'
AND usersId='.$usersId.'"
That wasn't working out well. It felt like the sessions wasn't finding the id of the current $_SESSION.
My search .php looks like this:
$usersId = $_SESSION['userId'];
$output = '';
if (isset($_GET['search']) && $_GET['search'] !== ' ') {
$searchingq = $_GET['search'];
$q = mysqli_query($conn, "SELECT * FROM files WHERE `usersId` = {$usersId} AND (`name` LIKE '%{$searchingq}%' OR `title` LIKE '%{$searchingq}%' OR `forfatter` LIKE '%{$searchingq}% )");
$c = mysqli_num_rows($q);
if($c == 0) {
$output = '<p>No search results for: "' .$searchingq. '"</p>';
} else {
while($row = mysqli_fetch_array($q)) {
$name = $row['name'];
$title = $row['title'];
$forfatter = $row['forfatter'];
$download = $row['downloads'];
$output .=
Help is very appreciated!
When doing code like this, I like to first run the query itself just to get it right.
This type of filtering should be easy because you already have each file associated to a user.
The base query would be like this:
Assuming you are looking for files for user 99.
This is the basic query and is intended only to show how to get the files for a user. This does not go into the code.
SELECT * FROM files
WHERE `usersId` = 99
This would give you only files that belong to that user. Again, this is only to explain the query, not to go in the code.
Now let's add to the above query to include the search criteria. Assume we are looking for 'foo'
SELECT * FROM files
WHERE `usersId` = 99
AND (`name` LIKE '%foo%'
OR `title` LIKE '%foo%'
OR `forfatter` LIKE '%foo%' )
We now have a query we can use in code. Try this out by running it directly against your database.
This query says to find all the files for that user when any of the columns match the search criteria. You can, therefore, write this in PHP something like this:
(Some code has been removed for the sake of brevity)
$usersId = $_SESSION['userId']; // the user id of the logged in user.
// code removed
$searchingq = $_GET['search']; // the search string
$q = mysqli_query($conn, "
SELECT * FROM files
WHERE `usersId` = {$usersId}
AND (`name` LIKE '%{$searchingq}%'
OR `title` LIKE '%{$searchingq}%'
OR `forfatter` LIKE '%{$searchingq}% )";
while($row = mysqli_fetch_array($q)) {
// Do output here
}
The following is how to do it with prepared statements.
You can learn more about Prepared Statements here: https://www.php.net/manual/en/mysqli.quickstart.prepared-statements.php
$stmt = $mysqli_prepare($conn, "
SELECT * FROM files
WHERE `usersId` = ?
AND (`name` LIKE '%?%'
OR `title` LIKE '%?%'
OR `forfatter` LIKE '%?% )");
// Replace the ?'s with the actual values.
mysqli_stmt_bind_param($stmt, "isss", $usersId, $searchingq, $searchingq, $searchingq);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
while ($row = mysqli_fetch_assoc($result)) {
// output goes here
}
Maybe you could try adding AND usersId=".$userId to the end of your query.

What is the correct MySQL syntax to retrieve data with multiple parameters

I am retrieving data from a database with php and MySQL as follows
$query = mysql_query("SELECT * FROM pictures WHERE (title LIKE '%$Search%' OR keywords LIKE '%$Search%') AND approved = 'YES' ORDER BY title ASC");
The query is correct and there are no errors and the query works fine for "title LIKE '%$Search%'" but the parameter "OR keywords LIKE '%$Search%'" is not retrieving data. The parameter "AND" also works correctly.
The keywords are stored in the database for example "pizza, restaurants, take away" but I don't see that is a problem.
My question is "What is the correct syntax for applying the "OR" parameter?
Remove the brackets around (title LIKE '%$Search%' OR keywords LIKE '%$Search%')
Those are generally used for subqueries.
$query = mysql_query("
SELECT * FROM pictures
WHERE title LIKE '%$Search%'
OR keywords LIKE '%$Search%'
AND approved = 'YES'
ORDER BY title ASC
");
https://dev.mysql.com/doc/refman/5.0/en/subqueries.html
Here is an example of a subquery, and pulled from the manual on MySQL.com:
SELECT * FROM t1 WHERE column1 = (SELECT column1 FROM t2);
Edit:
Or try a different quoting method:
$query = mysql_query("
SELECT * FROM pictures
WHERE title LIKE '".%$Search%."'
OR keywords LIKE '".%$Search%."'
AND approved = 'YES'
ORDER BY title ASC
");
You could also try escaping your data:
$Search = mysql_real_escape_string($Search);
as an example. I don't know how you're assigning that variable.
phpMyAdmin test edit:
This is what I used inside phpMyAdmin:
SELECT * FROM table
WHERE col1 LIKE '%pizza%'
OR col2 LIKE '%pizza%'
AND col3 = 'YES'
ORDER BY col1 ASC
using pizza as the search keyword seeing that $Search will be based on the same keyword for you, where columns contain "large pizza" in one, and "pizza, take away, restaurants" in another.
Remember that, whatever you're using/assigning $Search to, must reside inside all your queried columns.
You may also want to make use of explode().
Here is an example pulled from https://stackoverflow.com/a/15289777/
<?php
$search = 'Gold Chain Shirt';
$bits = explode(' ', $search);
$sql = "SELECT name FROM product WHERE name LIKE '%" . implode("%' OR name LIKE '%", $bits) . "%'";
The above will generate this query:
SELECT name FROM product WHERE name LIKE '%Gold%' OR name LIKE '%Chain%' OR name LIKE '%Shirt%'
Sorry for taking some time but this is my working answer to my own question... not the prettiest syntax but it works without any string functions or explode functions. MySql can handle keywords quite well without any other functions being included:
$query = mysql_query("SELECT * FROM pictures
WHERE
title LIKE '%$Search%' AND featured IS NOT NULL AND streetview IS NOT NULL AND (id_user > '1') AND (status = '1')
OR
keywords LIKE '%$Search%' AND featured IS NOT NULL AND streetview IS NOT NULL AND (id_user > '1') AND (status = '1') ORDER BY title ASC");
Thank you all for your contributions

php MySQL Search on two columns

I'm trying to do a search function using php and MySQL. I need to search from two different columns, one for song title and one for artist and user can search using:
1. artist name.
2. song title.
3. both of them (without order)
4. there will be special characters for ex: ' (apostrophe)
This is what I have done but I need a more efficient way to do this. I have also thought using similar_text(); in php. Can anyone suggest me a better way to do this. Thank You
table: songs
|artist | title |
|----------------------|
|eminem | not afraid |
|kida | o'najr |
My code:
$search_value = $_POST["search_value"];
$query = "select * from `songs` where concat(`title`, `artist`) like '%$search_value%'";
You should use the SQL OR statement, better than CONCAT one in this case.
Combined with a space before and after what you search, this should give you the expected result !
(I mean if you search for 'raid' you will not find 'Eminem - Not Afraid', if you want to find it you have to search for 'afraid' for exemple ; If you want to sort the results by revelance, you will have to create indexes and use them, or use Levenshtein method or something else ...)
Don't forget to escape your data before using it in sql statements.
Btw if you want to make it case insesitive you will have to use blabla COLLATE UTF8_GENERAL_CI LIKE %what you search% blabla
// if you are using mysql_connect()
$search_value = ' '.mysql_real_escape_string($_POST["search_value"]).' ';
$query = "select * from `songs` where `title` like '%$search_value%' or `artist` like '%$search_value%'";
// if you are using PDO
$args[':search_value'] = '% '.$_POST["search_value"].' %';
$query = "SELECT * FROM `songs` WHERE `title` LIKE :search_value OR `artist` LIKE :search_value";
$qry = $PDO->prepare($query);
$res = $qry->execute($args);
For a multi-words search you can also use
// for mysql_connect()
$search_value = $_POST["search_value"];
$search_value = explode(' ', $search_value);
foreach($search_value as $k => $v){
$search_value[$k] = mysql_real_escape_string($v);
}
$search_value = ' '.implode(' % ', $search_value).' ';
// for PDO
$search_value = explode(' ', $_POST["search_value"]);
$search_value = implode(' % ', $search_value);
$args[':search_value'] = '% '.$search_value.' %';
You can simply use sql or statement if you don't want confusion .
$search_value = $_POST["search_value"];//check user input first than create a query.
$query = "select * from `songs` where `title` like '%$search_value%' or `artist` like '%$search_value%'";

NOT LIKE multiple OR statements?

if have a mailing script that i've set to throttle domains i send to. the remaining domains that do not have a throttle rate set, get assigned a default rate. to find domains which do NOT match an existing throttled domain, i use this php:
$query = "SELECT * FROM `mailer_lists` ml JOIN `mailer_controller` mc";
$query .= " ON ml.project_name = mc.project_name";
$query .= " WHERE `email` NOT LIKE '%";
$query .= "" . implode("' OR '%", $throttle_domain) . "'";
echo "$query";
the output of the echo is:
SELECT * FROM `mailer_lists` ml JOIN `mailer_controller` mc ON ml.project_name = mc.project_name WHERE `email` NOT LIKE '%gmail.com' OR '%hotmail.com' OR '%yahoo.com' OR '%aol.com'
as far as i can tell the output looks perfectly fine. i ran a test query in phpmyadmin and only the first domain applies. anything after the OR is ignored.
is there something obviously wrong with this query that i'm missing? can't you use multiple OR statements while using the NOT LIKE statement?
I would do
where substring_index(`email`,'#',-1) not in ('yahoo.com','gmail.com')
and so on.
You're taking mySQL's "human language" approach too far. :) Each OR is interpreted as an expression of its own, not as a value to do a LIKE against.
Also, I think you want to use AND. Using OR will always apply to every row.
Try
(`email` NOT LIKE "%gmail.com")
AND (`email` NOT LIKE "%hotmail.com")
AND (`email` NOT LIKE "%yahoo.com") ....
You must repeat the field_name not like part :
SELECT *
FROM `mailer_lists` ml
JOIN `mailer_controller` mc ON ml.project_name = mc.project_name
WHERE
`email` NOT LIKE '%gmail.com'
and `email` NOT LIKE '%hotmail.com'
and `email` NOT LIKE '%yahoo.com'
and `email` NOT LIKE '%aol.com'
LIMIT 50
(And you probably have to use and, instead of or, between the conditions)

select ... where id = any value. is it possible?

look at this table please
table
|id| |name| |order|
i must get the rows, where name = something and order = somevalue
so i write
select `id` from `table` where `name` = 'something' and `order` = 'somevalue'
but depend on php logic, sometimes i need to get all rows, where name = something, independently of order value. i don't want to change the query structure, because in practise there are many number of fields, and possible count of queries will become very big. so i want to save the structure of query, and when i need to select just by name, i want to write something like this:
select `id` from `table` where `name` = 'something' and `order` = any value
is it possible?
thanks
Well, it's kind of a hack, but if you really need to do this, it'll work like this:
select `id` from `table` where `name` = 'something' and `order` = `order`
Then you're just saying "wherever order is the same as itself", so it's always true.
No, this is not possible. You need to change the structure (optionally to a LIKE so you can use '%', but that's very ugly).
However, you don't need to write a different query to handle every possible combination. You can simply create the query dynamically:
//create base query
$query = "select `id` from `table` where `name` = 'something' ";
//add order if we need it
if ($use_order)
$query .= "and `order` = 'somevalue' ";
//repeat for any other optional part
Note that you should of course still take proper measures to avoid SQL injection and other security issues - I have not included this here in order to keep things simple.
If you are using bound parameters, it would be impossible.
If you just substitute the values, you can do the following:
select `id` from `table` where `name` = 'something' and `order` = `order`
This is a common theme with database queries - you need a variable query depending on how much filtering you wish to apply to the data it queries. You could go the route of having your query repeated as a string throughout your code, but that is bad practice as it increases the complexity of the code needlessly. Chances for errors occur if you need to change the query for some reason, and have to change it in multiple places as a result.
The better solution is to create a function which builds the query for you execute:
function buildMyQuery($name, $order = null) {
$sql = "SELECT `id` FROM `table` WHERE `name`='$name'";
if ($order != null) {
$sql .= " AND `order`='$order'";
}
return $sql;
}
You could then run this for just using the 'name' field:
$query = buildMyQuery("somename");
Or this for using both fields:
$query = buildMyQuery("somename", "someorder");
As someone mentioned above, this code is deliberately simplified and contains no contingency for possibly dangerous data passed in via $name or $order. You would need to use mysql_real_escape_string or something similar to clean the data first, at the beginning of the function before either piece of data is used.
Dynamic query generation is a fact of life as Byron says, so I would become accustomed to it now rather than using hack-ish workarounds.
I don't think you have any choice... Once you do a selection you can't "unfilter" and get more rows.
You should just use two queries-- either two independent queries, or one that selects on the name into a temp table, and then (optionally) one that further selects on the order attribute.
Like Chad said above, just set the column equal to itself. But be careful, on some platforms / install configurations, NULL != NULL:
select `id` from `table` where `name` = 'something' and coalesce(`order`,'') = coalesce(`order`,'')
On reflection, I have a better answer. My colleague showed me a way this can be done.
My example...
Select rentals.* From rentals Where ((? = '') OR (user_id = ?))
The variables must be the same.
If they are both 5 for example, the first boolean will be false, but the second will be true, for the rows where the users id is 5.
If you require "all", setting as an empty string will result in all rows being seen to meet the where clause condition.
Can't you just use a not null query here?
select `id` from `table` where `name` = 'something' and `order` is not null;
You should be able to do it like this:
select `id` from `table` where `name` <>'' and `order` <>''
That will select anywhere that the value is not equal to blank.
$sql = "SELECT * FROM auctions WHERE id = id ";
if ($category !== "ANY") {
$sql .= "AND category = $category "; }
if ($subcategory !== "ANY") {
$sql .= "AND subcategory = $subcategory "; }
if ($country !== "ANY") {
$sql .= "AND country = $country "; }
$sql .= "ORDER BY $order $sort LIMIT $limit OFFSET $offset";

Categories