I want to make a product search engine where the user types in a product code and it will bring back the result, this is easy.
But, I want to be able to compensate for numbers that look like letters and vice versa.
E.g a user types 6O12l, but the product code is actually 60121.
What do I need to put in the SQL query to bring back all products with 6O12l and/or 60121?
So far I have this which isn't working, it keeps bringing back the same result everytime no matter what I type in:
$searchString = $_POST['query'] ;
$searchString = preg_replace('#\W#', '', $searchString);
$firstLetter = substr($searchString, 0, 1) ;
include("db.php") ;
$result = $dbh->prepare("SELECT productCode
FROM products
WHERE productCodeREGEXP '6[O0]12[1l]'
AND productCode LIKE '$firstLetter%'") ;
$result->execute() ;
while($row = $result->fetch(PDO::FETCH_ASSOC)) {
echo $row['productCode'].'<br />' ;
}
I have managed to get it working, but I have encountered a new problem.
I'm using str_replace to substitute the letters for numbers and viceversa in the users query string, but it will only work for one or the other, not both:
$qString = str_replace(array('o', 'l', '0', '1'), array('[O0]', '[1l]', '[O0]', '[1l]'), $searchString) ;
Which gives me a mangled output of e.g. A[[1l]l]BC
Instead of A[1l]BC
Do you have product codes with letters? You can translate the query string to all numbers before you run the query. That's the easiest thing to do, and will be much faster than testing for both.
You can't search the database efficiently with regular expressions. However, you can transform your data for storage in a normalised form, and search using a normalised query string e.g. all O's to zeros, I and l's to ones and so on.
Use this:
SELECT * from products
where code REGEXP '6[O0]12[1l]'
I solved it :D
For reference, I found this function on PHP.net:
function search_replace($s,$r,$sql)
{ $e = '/('.implode('|',array_map('preg_quote', $s)).')/';
$r = array_combine($s,$r);
return preg_replace_callback($e, function($v) use ($s,$r) { return $r[$v[1]]; },$sql);
}
Another option
// regex expresssion
// str_replace goes in order, first change letters to numbers, then change to the regex
// 6012ol becomes 6[0O][1l]2[0O][1l]
$regexString = str_replace(array('o', 'l', '0', '1'), array('0', '1', '[0O]', '[1l]'), $searchString);
// like expression, allows the database to make the initial filter, _ is the single character match
// 6012ol becomes 6__2__
$likeString = str_replace(array('o', 'l', '0', '1'), '_'), $searchString);
$filt1 = "(productCode LIKE '$likeString%')"; // last % allows for partial matches
$filt2 = "(productCode REGEXP '$regexString')";
// now query, with the like filter first
$dbh->prepare("SELECT productCode
FROM products
WHERE $filt1 AND $filt2
") ;
Related
I've got a wordpress installation where the post slugs are 'r1', 'r2', 'r3', 'bah1', 'bah2', 'bah3', and so on. The initial letter is the category slug and the number is the number of the post in that category.
What I'm trying to create now is: save a post slug (say 'r' or 'bah' to a PHP string, then write a PHP script that selects post_name from wp_posts where only the numbers are output, sorted in descending order to get the highest value of posts first.
What I'm thinking is basically to somehow use the string length in PHP to get the number of characters to deduct from the beginning of the results, but I'm having problems getting it done right, especially when it comes to introducing the PHP string into the SQL search.
So far I only have the code to select all the posts that contain the right slug, but I've been unable to remove the prefix. Here's what I have:
$slug = 'r';
$con = #mysqli_connect("localhost","user","password","database");
$result = mysqli_query($con,"
select post_name from wp_posts
where post_name like '".$slug."%'
and post_type='post'
order by post_name desc");
while ($row = mysqli_fetch_array($result)) {
echo $row['post_name'] . ' ';
}
At the moment, the output is, predictably:
r9
r8
r7
r63
r62
r61
r60
r6
r59
r58
r57
r56
r55
r54
since SQL doesn't sort "correctly" and sees r6 as a smaller number than r61. That's part of the reason I want to strip 'r' from the results so I can sort them better.
Any thoughts?
You have a simple pattern here: some non-digit chars following by some digits. It's very easy to parse with regular expression like /\D+(\d+)/, removing all preceding non-digit chars by substituting with a backreference and PHP preg_replace function.
Example:
<?php
$num = preg_replace('/\D+(\d+)/', '$1', 'r12');
$num1 = preg_replace('/\D+(\d+)/', '$1', 'bah4');
$num2 = preg_replace('/\D+(\d+)/', '$1', 'bah38');
echo $num . "\n";
echo $num1 . "\n";
echo $num2 . "\n";
So you may do this in your while loop like this:
while ($row = mysqli_fetch_array($result)) {
$stripped[] = preg_replace('/\D+(\d+)/', '$1', $row['post_name']);
}
Then you may use PHP sort function to sort the resulting array.
What you need is the mysql function SUBSTRING (MySQL manual). Convert the result of that to integer using CAST (MySQL manual) to be able to sort on it later.
You SQL query could look like this:
SELECT post_name, CAST(SUBSTRING(post_name FROM x+1) AS UNSIGNED) AS post_number
FROM wp_posts
WHERE
post_name LIKE '".$slug."%' AND
post_type='post'
ORDER BY post_number DESC
Replace x with the length of your string.
You should also think about using prepared statements with mysqli. See How can I prevent SQL injection in PHP? for more info on this!
I need to remove some all numbers, some special characters and specific parts of text from my database. I have a script that can remove one character at a time but I would like to remove all characters in one time.
The characters and text I need to remove look like this: Dollar, Sterling, Page, .p, ~, . and then the numbers 1-9
For example: I have an entry right now that looks like this: This is a great book, 595 pages .p 595 and costs only $ 54.95
I need to update it so it looks like this: This is a great book, and costs only.
Below is the code snippet for the replace feature I use. I would like to use this set up as I want it to work with the rest of the script. $table is the name, $field is the column name, $search is the value that needs to be replaced and $replace is the value to replace with (blank in my case)
if($typeOK) {
// CREATE UNIQUE HANDLE FOR update_sql_array
$handle = $table.'_'.$field;
if($queryType=='replace') {
$sql[$handle]['sql'] = 'UPDATE '.$table.' SET '.$field.' = REPLACE('.$field.',\''.$search.'\',\''.$replace.'\')';
} elseif($queryType=='') {
$sql[$handle]['sql'] = 'UPDATE '.$table.' SET '.$field.' = REPLACE('.$field.',\''.$keyword1.'\',\''.$replace1.'\')';
}
I can post the whole script if needed.
I have tried using multiple replace statements but when I do so it only executes the last replace statement ignoring the first one.
Some databases support a function called TRANSLATE, but unfortunately, MySQL is not one of them. You can do it with nested replaces:
set field = replace(replace(replace( . . . (field, '0', ''), '1', '') . . . , '9', '')
Not elegant, but it works.
The "..." are intended to show additional calls to replace, as in the following sequence:
set field = replace(field, '0', '')
set field = replace(replace(field, '0', ''), '1', '')
set field = replace(replace(replace(field, '0', ''), '1', ''), '2', '')
. . .
Couldn't find an exact answer to this question on here, though it may be simple (if there is an answer please point me in the right direction).
I am pulling some data from MySQL and some of the characters are causing the data not to be displayed at the end point. I therefore need to single out these specific characters and replace them with a permitted character. Specifically I need to change & , ' and + . I have to do this working from this query:
$query = "select * from data where a_data_id=".$ID." AND a_discard_data_from!=1";
I was wondering if I can add to this string, or after this string, some rule to replace instances & , ' or + with another character/s.
Thanks for looking!
NOTE: There is no problem with the string above, it is functioning fine, I just want to add to it or after it some type of code that will replace certain characters in the data pulled from the query
Surrounding Code:
$arr="";
$main_arr="";
$query = "select * from data where a_data_id=".$ID." AND a_discard_data_from!=1";
$query .=" and last_update >= " . '"' . $dataDate . '"';
$table = mysql_query($query);
if (mysql_num_rows($table) > 0) {
while ($row = mysql_fetch_assoc($table)) {
foreach ($row as $key => $value) {
$arr[$key] = $value;
if ($key == "to_data_id") {
$array=mysql_fetch_array(mysql_query("select name,additional from details where data_id=".$value));
$arr["data_id"] = $array['additional'].".".$array['name'];
}
}
$main_arr[] = $arr;
}
}
$data = json_encode($main_arr);
Just how to slow it into this code would be great, thank you! (This code works fine, just want to know what I could change / add to replace those characters).
UPDATE: Is anyone able to give me an answer with how I might be able to use strtr()function to replace the results please? Thanks again for the responses!
In mysql if you manually select a field after selecting * it overwrites that field, so provided there's only one field you want to run replaces on and you know the field you know which field it is in advance you can change your select query to (the somewhat ungraceful):
"select
*,
REPLACE(
REPLACE(
REPLACE(`data`.`YOURFIELDNAME`, '&', 'A'),
',', 'A'),
'\'', 'A')
from
data
where a_data_id=".$ID." AND a_discard_data_from!=1";
Which will remove those characters. This is not very graceful, and as eggyal pointed out, you're better off doing this on the front end.
Ok, here's an easy one:
I have a table in MySQL where I keep some country names (inserted by the user). The "problem" is that some of the users inserted the country name having no first capital letter.
A need a script (query+PHP) that selects all the values from 1 column inside the table, and apply a PHP script that makes the first letter of every word a capital letter, and the rest in small letter.
Example:
Before edit:
Name oNe
cOUNTRY
VaLue
value
After edit:
Name One
Country
Value
Value
I want to know two things:
->easiest way to edit a value inside a mySQL table
->easiest PHP way to capitalize the first letter of every word (space separated) inside a script
Thanks!
You can use this function:
DELIMITER $$
DROP FUNCTION IF EXISTS `ICap`$$
CREATE FUNCTION `ICap`(mystring varchar(1000))
RETURNS VARCHAR(1000)
BEGIN
DECLARE i INT DEFAULT 1;
DECLARE myc, pc CHAR(1);
DECLARE myoutstring VARCHAR(1000) DEFAULT LOWER(mystring);
WHILE i <= CHAR_LENGTH(mystring) DO
SET myc = SUBSTRING(mystring, i, 1);
SET pc = CASE WHEN i = 1 THEN ' ' ELSE SUBSTRING(mystring, i - 1, 1) END;
IF pc IN (' ', '&', '''', '_', '?', ';', ':', '!', ',', '-', '/', '(', '.') THEN
SET myoutstring = INSERT(myoutstring, i, 1, UPPER(myc));
END IF;
SET i = i + 1;
END WHILE;
RETURN myoutstring;
END$$
DELIMITER ;
After that, you can retrieve results like this:
SELECT
ICap(name)
FROM
MyTable
or just update all the rows:
UPDATE
MyTable
SET
name = ICap(name)
To fix the database with only one sql statement use this:
UPDATE country_table SET country_name = CONCAT(UCASE(MID(country_name,1,1)),MID(LOWER(country_name),2));
This will uppercase the first letter of each country name and lowercase the rest. All without needing any PHP at all.
->The easiest way to edit a value inside a mySQL table
UPDATE TABLE SET COL="NEW VALUE" where COL="OLD VALUE";
Be careful, it will replace all line wher COL is equal to OLd VALUE
->easiest PHP way to capitalize the first letter of every word (space separated) inside a script
There's a function for that : http://php.net/manual/en/function.ucwords.php
To Edit the values in in the database just run a Update Query (I don't know how your table is designed) from php -- http://www.w3schools.com/php/php_mysql_intro.asp.
To Capitalize the first character of every word you can use ucwords.
You can do it all in mysql if you want:
SELECT CONCAT(UPPER(SUBSTRING(CountryName, 1, 1)), LOWER(SUBSTRING(CountryName FROM 2))) from countries AS properCountryname;
The resultset returned would be your CountryName field with uppercased first letter.
You coud even do the update in one sql query, if you wanted.
If you really want php to do the job, something like this:
<?php
set_time_limit(0);
$cnt=0;
$con=mysql_connect('localhost','root','');
mysql_select_db('fakeDB',$con);
$query="SELECT `name`,id FROM countries";
$result=mysql_query($query);
while ($row=mysql_fetch_array($result))
{
$name= $row["name"];
$key = $row["id"];
$field = strtolower($name);
$array=explode(" ",$name);
for ($i=0;$i<count($array);$i++)
{
$array[$i] = ucfirst($array[$i]);
}
$field = implode(" ",$array);
$query2 = "UPDATE countries SET `name`='$name' where id=$key";
mysql_query($query2);
$cnt++;
}
echo $cnt." Records updated";
?>
Just a quick untested thingie but it should give you an idea.
Right now I'm just using a simple
WHERE name LIKE '%$ser%'
But I'm running into an issue - say the search is Testing 123 and the "name" is Testing, it's not coming back with any results. Know any way to fix it? Am I doing something wrong?
If you want to search for 'Testing' or '123' use OR:
WHERE (name LIKE '%Testing%' OR name LIKE '%123%')
Note however that this will be very slow as no index can be used and it may return some results you didn't want (like "4123"). Depending on your needs, using a full text search or an external database indexing product like Lucene might be a better option.
That's how LIKE works - it returns rows that completely contain the search string, and, if you use "%" optionally contain something else.
If you want to see if the field is contained in a string, you can do it this way:
SELECT * FROM `Table` WHERE "Testing 123" LIKE CONCAT("%",`name`,"%")
As Scott mentioned, you cannot check to see if the search contains the column value, it works the other way round.
so if $ser = "testing" and table has a row name = testing 123 it will return
For what you're trying to do you'll need to tokenize the search query into terms and perform an OR search with each of them or better still check out mysql full text search for a much better approach
After the variable $ser is replaced, the query is:
WHERE name LIKE '%Testing 123%'
You should build the query separating by words:
WHERE name LIKE '%$word[1]%$word[2]%'
not efficient (as your example) but working as you want:
WHERE name LIKE '%$ser%' OR '$ser' LIKE CONCAT('%', name, '%')
As mentioned by Mark and others, a full text search method may be better if possible.
However, you can split the search string on word boundary and use OR logic—but check for the whole string first, then offer the option to widen the search:
NOTE: Input sanitization and preparation not shown.
1. Query with:
$sql_where = "WHERE name LIKE '%$ser%'";
2. If zero results are returned, ask user if they would like to query each word individually.
3. If user requests an 'each word' search, query with:
$sql_where = get_sql_where($ser);
(Working) Example Code Below:
$ser = 'Testing 123';
$msg = '';
function get_sql_where($ser){
global $msg;
$sql_where = '';
$sql_where_or = '';
$ser = preg_replace("/[[:blank:]]+/"," ", trim($ser)); //replace consecutive spaces with single space
$search_words = explode(" ", $ser);
if($search_words[0] == ''){
$msg = 'Search quested was blank.';
}else{
$msg = 'Search results for any of the following words:' . implode(', ', $search_words);
$sql_where = "WHERE name LIKE '%$ser%'";
foreach($search_words as $word){
$sql_where_or .= " OR name LIKE '%$word%'";
}
}
return $sql_where . $sql_where_or;
}
$sql_where = get_sql_where($ser);
//Run query using $sql_where string