sql query to search from 2 fields mysql Fulltext - mysql

I have a search that searches for songs in a database, currently they can search by artist or the title of the song. So at the moment they could search tom jones or its not unusual they couldn't search tom jones unusual. How do I make this possible my current SQL looks like this
SELECT *
FROM songs
WHERE artist LIKE '%$search%'
OR songname LIKE '%$search%'
LIMIT 6
I have added full text search to the artist and songname columns. Can't work out how to search on both rows.

Looks like you are using tom jones unusual as your search text '$search'. When you are using like in mysql it will search for the text pattern in the given field but when you enter tom jones unusual as the search text there are no occurrences for such a text. Because tom jones is in one field and unusual is in the other field
You can do following to get done what you want
get the full search text tom jones unusual
split the search text from the white spaces in to separate variables ex $ser1 = tom; $ser2 = jones; $ser3 = unusual;
then do something like this in mysql
select * from song where artist like '%$ser1%' or '%$ser2%' or '%$ser3%' or songname like '%$ser1%' or '%$ser2%' or '%$ser3%'

The solution to this is using full text search. You first need to define the rows you want to use in full text search you can do this with the following mysql command
ALTER TABLE songs
ADD FULLTEXT(artist, songtitle)
and then the sql query is
$sql = "SELECT * FROM songs WHERE MATCH (songname, artist) AGAINST ('$search') LIMIT 6";
This will return results only when there is a matching word if you want like results before a full word is typed you can so something like this.
$sql = "SELECT * FROM songs WHERE MATCH (songname, artist) AGAINST ('$search' IN BOOLEAN MODE) LIMIT 6";
$sqllike = "SELECT * FROM songs WHERE artist like '%$search%' OR songname like '%$search%' LIMIT 6";
$result = mysqli_query($con, $sql);
$resultlike = mysqli_query($con, $sqllike);
if (mysqli_num_rows($result) > 0) {
// output data of each row
while($row = mysqli_fetch_assoc($result)) {
echo "<a href='". $row["songid"] . "' class='songclicker'>";
echo "<div class='song whitefont'>";
echo "<img src='" . $row["artwork"]."' class='songimage'>";
echo "<p>". $row["songname"] . "</p>";
echo "<p>". $row["artist"] . "</p></div>";
}
echo "<a href='". urlencode($search) ."'><h1 class='seeall'>See all search results</h1></a>";
} else {
if (mysqli_num_rows($resultlike) > 0) {
// output data of each row
while($row = mysqli_fetch_assoc($resultlike)) {
echo "<a href='". $row["songid"] . "' class='songclicker'>";
echo "<div class='song whitefont'>";
echo "<img src='" . $row["artwork"]."' class='songimage'>";
echo "<p>". $row["songname"] . "</p>";
echo "<p>". $row["artist"] . "</p></div>";
}
echo "<a href='searchresults.php'><h1 class='seeall'>See all search results</h1></a>";
} else {
echo "0 results";
}
}

Related

Query with REGEXP without observance of order of words

First of all I want to excuse me for any english mistakes that might be in my texts, in fact it is not my mother language so it's not really perfect... Anyways:
I am using a table for search tags which contains all search tags and a unique name they belong to. For the actual search query, I use REGEXP (the query is written in PDO style). The user has to type in the search keywords seperated with a comma.
As I executed several tests I noticed that the shown results are depending on the order of the typed in words and the order the search tags are stored in the seach tags table.
My question is: How can I execute a query using REGEXP which does ignore the order of the words?
Till now the actual query is pretty simple:
$search = explode(',', htmlentities($_POST["search"]));
$search = implode('|', $search);
$stmt = $db->prepare("SELECT name FROM blablabla WHERE name REGEXP :search");
$stmt->bindValue(":search", $search, PDO::PARAM_STR);
Thanks!
I am sorry, but I do not know, wether this is possible, at least in a performant way.
I would try to achieve it like that:
$search = explode(',', htmlentities($_POST["search"]));
$sql = 'SELECT name FROM blablabla ';
$query = array();
$parts = array();
foreach($search as $value) {
$query[] = 'name LIKE ?';
$parts[] = '%' . $value . '%';
}
$sql .= ' WHERE ' . implode(' OR ', $query); //Or use logical AND. Just what suits you
$stmt = $pdo->prepare($sql);
$stmt->execute($parts);

PHP explode and MySQL query to search in multiple columns

I have a form where I want a user to enter one or more words. These words should then match mutiple columns in a MySQL database.
I have started to build some code but I'm stuck.
<?php
$term = $_SESSION['session_searchstring']; //Let's say that session is John Doe
$searchterm = explode(' ',$term);
$searchFieldName = "name";
$searchCondition = "$searchFieldName LIKE '%" . implode("%' OR $searchFieldName LIKE '%", $searchterm) . "%'";
$sql = "SELECT * FROM students WHERE $searchCondition;";
echo $sql; //Echo to test what mysql_query would look like
?>
The above code will output:
SELECT * FROM students WHERE name LIKE '%John%' OR name LIKE '%Doe%';
The problem is that I want to search in multiple columns ($searchFieldName). I have for example
customer_firstname
customer_lastname
And I want to match my searchstring against the content of both columns.. How would I continue?
Perhaps
$term = $_SESSION['session_searchstring']; //Let's say that session is John Doe
$searchterm = explode(' ',$term);
$searchColumns = array("customer_firstname","customer_lastname");
for($i = 0; $i < count($searchColumns); $i++)
{
$searchFieldName = $searchColumns[$i];
$searchCondition .= "($searchFieldName LIKE '%" . implode("%' OR $searchFieldName LIKE '%", $searchterm) . "%')";
if($i+1 < count($searchColumns)) $searchCondition .= " OR ";
}
$sql = "SELECT * FROM students WHERE $searchCondition;";
echo $sql; //Echo to test what mysql_query would look like
Produces
SELECT * FROM students WHERE (customer_firstname LIKE '%John%' OR customer_firstname LIKE '%Doe%') OR (customer_lastname LIKE '%John%' OR customer_lastname LIKE '%Doe%');
If your table is of MyIsam type or you can convert it to MyIsam, use MySQL Fulltext Search. if not, anyway, you can build a long query like
SELECT * FROM students WHERE name LIKE '%John%' OR name LIKE '%Doe%' OR lastname LIKE "%John%" OR lastname LIKE "%Doe%"
or union your columns into one another just for search (but this both are not prefered).
Also a good approach is to use fulltext search engines like Sphinx.
In my case I needed all search phrases/terms to match at least one column, no search phrase/term could be a no-match.
I ended up tweaking the example from Kermit in the following way:
public function getRawWhereFilterForColumns($filter, $search_columns)
{
$search_terms = explode(' ', $filter);
$search_condition = "";
for ($i = 0; $i < count($search_terms); $i++) {
$term = $search_terms[$i];
for ($j = 0; $j < count($search_columns); $j++) {
if ($j == 0) $search_condition .= "(";
$search_field_name = $search_columns[$j];
$search_condition .= "$search_field_name LIKE '%" . $term . "%'";
if ($j + 1 < count($search_columns)) $search_condition .= " OR ";
if ($j + 1 == count($search_columns)) $search_condition .= ")";
}
if ($i + 1 < count($search_terms)) $search_condition .= " AND ";
}
return $search_condition;
}
I only needed the contents of the Where-clause since I'm using Laravel and could put that into the rawWhere-method.
usage:
$search_condition = $this->getRawWhereFilterForColumns
("John Doe", array("column1", "column2"));
which produces
(column1 LIKE '%John%' OR column2 LIKE '%John%')
AND (column1 LIKE '%Doe%' OR column2 LIKE '%Doe%')
And finally you use this $search_condition in whatever way suits you, for example:
$sql = "SELECT * FROM students WHERE $search_condition;";
Or in my Laravel-case:
$modelInstances = Model::whereRaw
($search_condition)->paginate(self::ITEMS_PER_PAGE);
Perhaps this is an improved solution for either David or anybody else visiting this thread, like me, even if it's almost two years after the original post.

Using DISTINCT to filter duplicates?

I have the following query, and would like to list only the first match.
$first = $_GET['category'];
$first = $first[0] . "%";
$query = mysql_query("SELECT * FROM lyrics WHERE authorclean LIKE '".$first."'") or die(mysql_error());
(?category=b)
So DISTINCT could do this right? This is what I tried, but did not work:
$query = mysql_query("SELECT DISTINCT authorclean FROM lyrics WHERE authorclean LIKE '".$first."'") or die(mysql_error());
EDIT: Here is the full code:
function getCategory() {
$first = $_GET['category'];
$first = $first[0] . "%";
$query = mysql_query("SELECT DISTINCT authorclean FROM lyrics WHERE authorclean LIKE 'B%'") or die(mysql_error());
//$query = mysql_query("SELECT * FROM lyrics WHERE authorclean LIKE '".$first."'") or die(mysql_error());
if(mysql_num_rows($query) == 0) {
echo "Geen resultaten gevonden.";
} else {
while ($row = mysql_fetch_assoc($query)) { ?>
<p><?= $row['author']; ?></p>
<?php }
}
}
(B% is just for testing)
If I run this following query in the database directly I get two results. If I run with the code above I just get an empty page (except for the html thats already there).
SELECT DISTINCT authorclean FROM lyrics WHERE authorclean LIKE 'B%'
You should use LIMIT 1 to list only the first match.
If you have a a table "tbl_lyrics" with fields: author lyrics year and is filled for example as follows:
author_A lyrics_A year_A
author_A lyrics_A1 year_A1
author_A1 lyrics_A2 year_A
author_B lyrics_B1 year_B1
if you do
select distinct(author) from tbl_lyrics where author like '%author_A%'
you are going to get: author_A and author_A1. NOT the first one that matches.
If you want the first one that matches you can do:
select author from (select distinct(author) as author from tbl_lyrics where author like '%author_A%') where rownum <2;
this will return author_A only.
Limit is used with MySql but would not work with oracle databases

how would I change this over to do a full text search instead of LIKE?

after some researching I put this code together to search a mysql table in the db. while it works fine, it limit itself to match the words exactly as the user enters it. anyone know how to make it so that it matches my some sort of relevancy? I have been reading about the full text search but I cant really seem to grasp it.
for example, if you search for 'unanswered questions' in two fields, I want to be able to get result like that include the searched word(s) in any string that it show up in, and list it according to relevancy, like so (search results example output):
- unanswered questions
- answered questions
- answer question
- unanswered questions
- unanswered questions
- questions
- answer
$k = trim ($_GET['search']);
$i = "";
$terms = explode (" ", $k);
$query = "SELECT * FROM table1 WHERE ";
foreach ($terms as $each){
$i++;
if ($i == 1)
$query .= "fld_title LIKE '%$each%' OR fld_keyword LIKE '%$each%' ";
else
$query .= "OR fld_title LIKE '%$each%' OR fld_keyword LIKE '%$each%' ";
}
// connect
include_once "connect.php"; //connect 2 db
$query = mysql_query($query);
$numrows = mysql_num_rows ($query);
if ($numrows > 0){
while ($row = mysql_fetch_assoc ($query)){
//
//
// echo out something here
//
//
}
}else
{
echo "No results found for <b>$k</b>";
}
to do a fulltext search you have to:
Create a Fulltext index in the table (note the fields can't be BLOB)
ALTER TABLE tablename ADD FULLTEXT(field1, field2,...);
in your case:
ALTER TABLE table1 ADD FULLTEXT(fld_title, fld_keyword);
in php change
$k = trim ($_GET['search']);
$i = "";
$terms = explode (" ", $k);
$query = "SELECT * FROM table1 WHERE ";
foreach ($terms as $each){
$i++;
if ($i == 1)
$query .= "fld_title LIKE '%$each%' OR fld_keyword LIKE '%$each%' ";
else
$query .= "OR fld_title LIKE '%$each%' OR fld_keyword LIKE '%$each%' ";
}
for
$k = trim ($_GET['search']);
$query="SELECT * FROM table1 WHERE MATCH(fld_title, fld_keyword) AGAINST ('".$k."')";
if you want to see the relevancy of the results:
$query="SELECT *, MATCH(fld_title, fld_keyword) AGAINST ('".$k."') as relevancy FROM table1 WHERE MATCH(fld_title, fld_keyword) AGAINST ('".$k."')";
The MATCH-AGAINST returns a number: 0 for no match or other depending on matching.
You can "order by relevancy", change the query for make more relevant the search... MATCH(fld_title, fld_keyword) AGAINST ('".$k."') > 0.5
Only one problem: the AGAINST part ($k for you) must be greater than 3 characters.

Correcting an UPDATE statement (and making it more secure!)

I'm trying to a single value in my DB...When I run it through the console, it works correctly (as I'm replacing the variables with numbers and text).. However, My query is not returning a value for book ID when I insert the PHP variable for it.. It's because the book_id is unpopulated...
$query = "UPDATE books "
. "SET readstatus='".$readstatus."' "
. "WHERE book_id=".$book_id;
echo $query
The echoed query states:
UPDATE books SET readstatus='half' WHERE book_id=0
The book ID is stored in the URI as bookstatusupdate.php?book_id=
Just cannot figure this one out!
It would help to know the error. Firstly, echo out the query:
$query = "UPDATE books "
. "SET readstatus='".$readstatus."' "
. "WHERE book_id=".$book_id;
echo $query;
I would guess that $book_id is unpopulated, so the query fails. What you should really be doing to make it secure is casting integers with (int) and wrapping strings in mysqli_real_escape_string().
$query = "UPDATE books "
."SET readstatus='". mysqli_real_escape_string( $readstatus )."' "
."WHERE book_id=". (int) $book_id;
If you're trying to get data from the URL, do it like so:
$book_id = (int) $_GET['book_id'];
$query = "UPDATE books "
."SET readstatus='". mysqli_real_escape_string( $readstatus )."' "
."WHERE book_id=". (int) $book_id;
echo $query;