Search query and ordering by matches with my sql - php

Hi am currently stuck here, been doing research on how to write a mysql statement for a flexible search for products and order them by relevance on a project am working on have seen a few but wasn't helpful please i need help on how to make it work, my current method doesn't work, here it is.
User types in search field and submits "iPad 3rd Generation".
My script breaks the string into words like so.
$termsExploded = array_unique(explode(' ', $term));
No i use php to create an sql query based on the number of words found.
$i = 0;
foreach ($termsExploded as $word) {
if (strlen($word)>1) {
if ($i == 0) {
$where_query = $where_query." name LIKE '%".$word."%'";
}
else{
$where_query = $where_query." OR name LIKE '%".$word."%'";
}
$i++;
}
}
The where query variable now looks like this.
name Like '%ipad%' Or name Like '%3rd%' Or name Like '%Generation%'
Now search for the products ids like so.
$IDs = "SELECT DISTINCT id FROM store_items WHERE".$where_query;
I now create a second where query based on the IDs returned like so
$where_query_s = null;
$i = 0;
foreach ($IDs as $result) {
$returnID = $result->id;
if ($i == 0) {
$where_query_s = $where_query_s." id = ".$returnID."";
}
else{
$where_query_s = $where_query_s." OR id = ".$returnID."";
}
$i++;
};
Now i select the products again based on the distinct IDs returned like so
$items = "SELECT * FROM store_items WHERE".$where_query_s;
Now this works to get the products but how can i sort it based on best match?

Assuming you want to order by the number of matches then build up another string as follows:-
ORDER BY IF(name Like '%ipad%', 1, 0) + IF(name Like '%3rd%', 1, 0) + IF(name Like '%Generation%', 1, 0) DESC
But this will be slow, and takes no account of indexing to improve performance nor of plural / singular (ie, it someone searches for 'flies' it won't rank 'fly' properly).
To put that more into code:-
$where_query = array();
$order_query = array();
foreach ($termsExploded as $word)
{
if (strlen($word)>1)
{
$where_query[] = " name LIKE '%".$word."%'"
$order_query[] = " IF(name Like '%".$word."%', 1, 0)"
}
}
$IDs = "SELECT DISTINCT id FROM store_items WHERE ".implode(' OR ', $where_query)." ORDER BY ".implode(' + ', $order_query)." DESC";

Arrange for your query to look like this:
select field1, field2, etc, count(*) records
from store_items
where blah blah blah
group by field1, field2, etc
order by records desc

If the table is MyISAM based or if it is InnoDB and the version is Mysql 5.6 or greater, then you can use full text search (see http://dev.mysql.com/doc/refman/5.6/en/fulltext-search.html)
effectively you want a query similar to
SELECT * FROM store_items WHERE MATCH (name) AGAINST ('iPad 3rd Generation')
ORDER BY MATCH (name) AGAINST ('iPad 3rd Generation')

Related

PHP/SQL: Faster Way to Combine Query Results

I'm joining data from two SQL queries and I'm wondering if there is a faster way to do this as a single SQL query because there is a lot of looping involved. I've got two queries that look for different string values in the "option_name" field:
$sql01= "SELECT user_id, option_value FROM wp_wlm_user_options WHERE option_name = 'wpm_login_date' ORDER BY user_id";
$sql02 = "SELECT user_id, option_value FROM wp_wlm_user_options WHERE option_name ='stripe_cust_id' ORDER BY user_id ";
Then I create two arrays:
//Process the 1st SQL query data into an Array
$result_array01 = array();
$j = 0;
while($r = mysql_fetch_assoc($result01)) {
if(!empty($r['option_value'])){
//User Id and Last Login
$result_array01[$j]['user_id'] = $r['user_id'];
$result_array01[$j]['last_login'] = $r['option_value'];
$j++;
}
}
//Process the 2nd SQL query data into an Array
$result_array02 = array();
$k = 0;
while($s = mysql_fetch_assoc($result02)) {
if(!empty($s['option_value'])){
//User Id and Stripe Customer Id
$result_array02[$k]['user_id'] = $s['user_id'];
$result_array02[$k]['cust_id'] = $s['option_value'];
$k++;
}
}
And finally, I combine the arrays:
//Combine the SQL query data in single Array
$combined_array = array();
$l = 0;
foreach($result_array01 as $arr01){
// Check type
if (is_array($arr01)) {
//mgc_account_print("hello: " . $arr01['user_id'] . "\r\n");
foreach($result_array02 as $arr02){
// Check type
if (is_array($arr02)) {
//Check if User Id matches
if($arr01['user_id'] == $arr02['user_id']){
//Create Array with User Id, Cust Id and Last Login
$combined_array[$l]['user_id'] = $arr01['user_id'];
$combined_array[$l]['last_login'] = $arr01['last_login'];
$combined_array[$l]['cust_id'] = $arr02['cust_id'];
$l++;
}
}
}
}
}
Why you doing in two different queries?
Use mysql IN('val', 'val2');
$sql01= "SELECT tbl1.user_id, tbl1.option_value FROM wp_wlm_user_options as tbl1 WHERE tbl1.option_name = 'wpm_login_date'
union all
SELECT tbl2.user_id, tbl2.option_value FROM wp_wlm_user_options as tbl2. WHERE tbl2.option_name ='stripe_cust_id' ";
But using OR/AND will your help you in your case , I didnt see at first that you want combined same table. I didnt delete my answer to help you for another solution
Also you should use DISTINCT to avoid multiple records.
SELECT DISTINCT USER_ID, OPTION VALUE FROM TABLE

Search with multiple tables & ORDER BY php mysql

I have a script i want to search multiple tables how can i do that.
**Also add ORDER BY function in Mysql query **
help is appreciated.
if(isset($_GET["search"]))
{
$condition = '';
//$query = explode(" ", $_GET["search"]);
$query = explode(" ", $_GET["search"]);
foreach($query as $text)
{
$condition .= "title LIKE +'%".mysqli_real_escape_string($connect, $text)."%' OR ";
}
$condition = substr($condition, 0, -4);
$sql_query = "SELECT * FROM countries WHERE " . $condition;
$result = mysqli_query($connect, $sql_query);
if(mysqli_num_rows($result) > 0)
{
while($row = mysqli_fetch_array($result))
{
echo '<tr><td>'.$row["title"].'</td></tr>';
}
}
else
{
echo '<label>Data not Found</label>';
}
}
SELECT * FROM (
(SELECT title FROM countries WHERE title LIKE '%mystring%')
UNION
(SELECT title FROM locations WHERE title LIKE '%mystring%')
) ta
That's the sql, but would need to point out the drawbacks of using union as a search tool.
a) The longer the tables get the longer the search will get, you can add in Limits on each query and then on the union as a whole - but it's not a winner.
b) The table columns have to match up so you'll need perhaps to do myID as ID, then you will need an extra column to say which each is (0=country, 1= location)
c) I guess you are trying to do a site search of sorts, in which case there isn't a relevance in this instance.
Hence I would use something like http://sphinxsearch.com/ bit tricky to get started, but a really quick search engine. If you have a large site. Otherwise look into mysql full text searches which also have relevence and are built in so easier to get started with.

How to return only items that occur in 2 sql select statemnts

I have two different sql statements. $sql grabs all the items whose title matches a certain search text. $cat_sql grabs all the category_items that are in a certain category. An item has an ID. A category_item has a field called item_id which is a foreign key to IDs in the items table
...
mysqli setup code
...
$title = $_POST["title"];
$cat_id = $_POST["cat_id"];
$cat_sql = "SELECT * FROM category_items WHERE category_id = '".$cat_id."'";
$sql = "SELECT * FROM items where title LIKE '%". $title ."%' Limit 70";
if (!$result_cat = $mysqli->query($cat_sql)) {
// The query failed.
echo "<h2 >ERROR</h2>";
exit;
}
if (!$result = $mysqli->query($sql)) {
// The query failed.
echo "<h2 >ERROR</h2>";
exit;
}
Then I display all items:
while ($item = $result->fetch_assoc()) {
include 'item_card.php';
}
Currently this just displays all items fetched in the $sql query. Is there some way to remove all items from $result that do not have their ID represented as an item_id in $result_cat?
NOTE:
I would strongly prefer not to do just combine both SELECT statements into a table join because the actual $sql and $cat_sql are not nearly as simple as I have represented here. Also, they vary depending on which if statement they are in.
My question is: given $result and $result_cat, can I remove items from $result?
EDIT 1
As suggested by comments I am making an array if item_ids then doing an in_array query. Progress thus far:
$result_cat_ids = [];
while ($cat_item = $result_cat->fetch_assoc()) {
$result_cat_ids[] = $cat_item['item_id'];
}
EDIT 2 Here is the working code following the suggestions in the comments
if (in_array($item['id'], $result_cat_ids)) {
include 'item_card.php';
}
You may also use 'INTERSECT' sql clause.
$sql = "SELECT * FROM items WHERE id IN (SELECT item_id FROM category_items WHERE category_id = '".$cat_id."' INTERSECT SELECT id FROM items where title LIKE '%". $title ."%')";
This way, you can query for items that accomplish both conditions.
Note: I'm not using "limit 70" but you may add it as well.

MySQL select from multiple identical tables and display results randomly with php

Hello I would like to query multiple identical tables in my db which has different prefixes and than display the results randomly but somehow I need to track the origin of the item and I couldn't figure out how
I do the query like this because I don't have access to information_schema
$query = "SHOW TABLES FROM mydb WHERE RIGHT( tables_in_mydb, 5 ) = 'table'";
$res = mysql_query($query);
$num = mysql_num_rows($res);
while($row = mysql_fetch_row($res)) {
$numbers = explode('_', $row[0]);
if($num > 0) {
$q = "SELECT `this`, `that`, `something` FROM ".$numbers[0]."_idetinticaltables"; // :)
$r = mysql_query($q);
while($c = mysql_fetch_array($r)) {
/*display the results randomly with an identifier where the come from*/
}
}
}
You could use ORDER BY RAND() to randomly sort it
The following might work:
Get the list of the tables you're interested in. You already do that.
Create a UNION of multiple SELECT statements. Each SELECT statement differs for the table being selected from and you add a column set to the name of the table (so you can identify it later):
(SELECT *, TABLENAME = 'first_name_of_table' FROM first_name_of_table ...)
UNION
(SELECT *, TABLENAME = 'second_name_of_table' FROM second_name_of_table ...)
UNION
...
ORDER BY RAND() LIMIT 10;
Because it is a UNION you can randomize the whole order then. See How can i optimize MySQL's ORDER BY RAND() function? because it is not that trivial to do well, the example above is only to have an ORDER BY and LIMIT clause placed there. With many entries in your tables, it will kill your server.
$aa=array()
while($c = mysql_fetch_array($r))
{
/*display the results randomly with an identifier where the come from*/
$aa[]=$c;
}
echo $aa; // print "Array"

mysql - if value = 0 shorten where statement

I wonder if it's possible to shorten query depending on some variable value in elegant way.
For example: I have value named $var = 0 and I would like to send a query that looks like this:
$query = "SELECT id, name, quantity FROM products WHERE quantity > 100";
But whan the $var != 1 I'd like to send a query like this:
$query = "SELECT id, name, quantity FROM products WHERE quantity > 100 AND id = '$var'";
So depending on value of $var I want to execute one of queries. They differ only with last expression.
I found two possible solutions but they are not elegant and I dont like them at all.
One is made in php:
if ( $var == 0 ) {
$query_without_second_expression
} else {
$query_with_second_expression
}
Second is made in mysql:
SELECT WHEN '$var' <> 0 THEN id, name, quantity
FROM products WHERE quantity > 100 AND id = '$var' ELSE id, name, quantity
FROM products WHERE quantity > 100 END
but i dont like it - each idea doubles queries in some whay. Can I do something like this?
SELECT id, name, quantity
FROM products WHERE quantity > 100
CASE WHEN $var <> 0 THEN AND id = '$var'
It's much shorter, and adds part of query if needed. Of course real query is much more complicated and shorter statement would be really expected. Anyone has an idea?
If I understand well..
$query = "SELECT id, name, quantity FROM products WHERE quantity > 100";
if ( $var != 0 ) {
$query .= " AND id = '$var'";
}
do you like it?
You could so something like this on the SQL side:
"SELECT id, name, quantity FROM products WHERE quantity > 100 AND (id = '$var' OR '$var' = 0)
But performance could be impacted. I would suggest building the appropriate query on the PHP side.
I'm no PHP developer (it is PHP, right?), but wouldn't it be easiest to build your query from a concatenated string?
Pseudo-code:
$my_query = "SELECT id, name, quantity FROM products WHERE quantity > 100"
if ($var != 1)
$my_query = $my_query + " AND id = '$var'";
end if;
/*go ahead with your query*/
You can do this:
SELECT id, name, quantity
FROM products
WHERE 1 = 1
AND ( $q IS NULL OR quantity > $q)
AND ( $var IS NULL OR id = $var)
If you want only the first condition to run then pass $q = 100 and $var = NULL, therefore the second condition will be ignored. And for the second query pass the $q = 100 and $var = id value and you will got the your second query.
If they only differ in additional where-statements, I would probably still stay in PHP and do the following:
$conditions = array();
$conditions[] = "(quantity > 100)"
if ($var == 0)
$conditions[] = "(id = '$var')";
if (some-other-expression)
$conditions[] = "(myfield = 'foo' OR myfield = 'bar')";
$sql = "
SElECT id, name, quantity
FROM products
WHERE
";
$sql .= $conditions.join(" AND ");
/ Carsten
I would use a ternary for this:
$query = "SELECT id, name, quantity FROM products WHERE quantity > 100" . ( $var != 0 ? " AND id = '$var'" : '' );
I'm an Oracle guy, but could you use the IF() function in your constraints, e.g.:
SELECT id, name, quantity
FROM products
WHERE quantity > 100
AND id = IF('$var'=0,'%','$var');
The '%' is a wildcard that would match anything, effectively ignoring the 2nd expression.
So if you are using the
If or the CASE
Why don't you use them in your sql query.
It would be some like
if ( $var == 0 ) {
$query_without_second_expression
}
else
{
$query_with_second_expression
}
But in you sql query.
DECLARE #var int
if(#var=1)
BEGIN
query 1
END
else
BEGIN
query 2
end
I guess this will solve your problem.
But as a personal advice try to make one query. Even with the variable. We don't believe in changing standard query depending on the conditions.
Still your wish and your desire
Cheers!!
For this particular problem, go with Paper-bat's simple condition append snippet.
However, if you have entirely different where statements, consider appending the appropriate where statement to the query once you know what you want. For instance,
$query = "SELECT id, name, quantity FROM products ";
if ( $var == 0 ) {
$query .= "quantity > 100";
} elseif {
$query .= "quantity > 120 AND id = '$var'";
} elseif {
...
}
...
It all depends on your needs, but neither this or Paper-bat's solutions duplicate query code.

Categories