I am using the following query (simplified for here) to check if a string contains a "watch-word" where the watch words are contained in a MySQL table:
$sql = "SELECT ww_id FROM watch_words WHERE ww_word IN (" . $string . ")";
This works perfectly for single words, but now I need to make it work for phrases (i.e. the field ww_word may contain more than one word). All I can think of are things like reading the whole table into an array and then doing multiple loops to compare against combinations of the words in the string, but I'm sure (hoping) there's a better way.
EDIT: Thanks for the suggestions, but as pointed out by Mike Brant, the needle is in MySQL and the haystack in PHP - not the "usual" way around (like a search form for instance). I need to check if a string (actually a message) contains one or more "watch phrases" - like a bad-language filter (but not that).
Sample table thus:
CREATE TABLE `watch_words` (
`ww_id` int(11) NOT NULL AUTO_INCREMENT,
`ww_word` varchar(250) NOT NULL,
PRIMARY KEY (`ww_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
INSERT INTO `watch_words` VALUES (1, 'foo bar');
INSERT INTO `watch_words` VALUES (2, 'nice sunny day');
INSERT INTO `watch_words` VALUES (3, 'whatever');
INSERT INTO `watch_words` VALUES (4, 'my full name here');
INSERT INTO `watch_words` VALUES (5, 'keyword');
So string "What a nice sunny day we're having" should return a match, whereas "What a lovely sunny day..." wouldn't. TIA.
use LIKE for pattern matching
$sql = "SELECT ww_id FROM watch_words WHERE ww_word LIKE '%" . $string . "%'";
or maybe interchange the two,
$sql = "SELECT ww_id FROM watch_words WHERE " . $string . " LIKE CONCAT('%', ww_word,'%')";
As a sidenote, the query is vulnerable with SQL Injection if the value(s) came from the outside. Please take a look at the article below to learn how to prevent from it. By using PreparedStatements you can get rid of using single quotes around values.
How to prevent SQL injection in PHP?
You will likely need to take a different approach here. You have the needle in MySQL and the haystack in PHP. Using things like LIKE (which you use for string matches not IN), MySQL can work fine with the haystack being in MySQL table and the needle in the application (in the LIKE).
There is no convenient reverse matching to pass MySQL the haystack and have it apply a needle from a field in a table against it.
You will likely need to select your needles out of the database and compare it to the haystack in your application.
Related
I'm having trouble with this snippet of code, and can't find any errors:
$query = "CREATE TABLE ? (? INT NOT NULL AUTO_INCREMENT, PRIMARY KEY(?), ? VARCHAR(30), ? VARCHAR(50), ? TIMESTAMP, ? VARCHAR(50), ? DECIMAL(15, 2), ? DECIMAL(3, 2), ? VARCHAR(255))";
$array = array($table_name, $id, $id, $a_title, $c_title, $date_updated_title, $s_title, $ds_title, $ps_title, $u_title);
try {
$results = db_query($db, $query, $array); // db_query() is my PDO function to query the database. This function works fine elsewhere.
echo($table_name . " create successfully!");
} catch (PDOException $e) {
echo('<br />Could not create table "' . $table_name . '".');
return false;
error($e); //error() is my function to write errors to my log, and works fine elsewhere.
}
When I run this in my browser, it returns my caught exception 'Could not create table "name".' However, I don't see any error in my log, so I don't know if it's a syntax issue, or what.
When I take the query itself, and replace the question marks with the actual values, and dump it in PHPMyAdmin, it creates the table fine. I'm not really sure what the issue is here. I've had reasonable success with PDO on another site, but I'm still relatively new. Any ideas?
Thanks for the help!
[Edit] I've since tried using this query:
"CREATE TABLE $a_title (? INT NOT NULL AUTO_INCREMENT, PRIMARY KEY(?), ? VARCHAR(30), ? VARCHAR(50), ? TIMESTAMP, ? VARCHAR(50), ? DECIMAL(15, 2), ? DECIMAL(3, 2), ? VARCHAR(255))";
I've tried with both single and double quotes. I also removed the $table_name variable from the array. Still getting a syntax error, and not sure why.
Comment from #DCoder is correct. You can use a query parameter only in place where you could normally put a single string literal, date literal, or numeric literal.
You can't use a query parameter for:
Table names
WRONG: SELECT * FROM ?
Column names
WRONG: SELECT * FROM table WHERE ? = 1234
Lists of values
WRONG: SELECT * FROM table WHERE column IN (?)
Though you could use IN() with a list of parameter placeholders, one for each scalar value.
SQL operators, expressions, or keywords
WRONG: SELECT * FROM table WHERE column ? 'value' AND ? ORDER BY column ?
For those cases, if you want dynamic content to become part of your query, the content must be part of the query before you call prepare().
But this means that you're back to interpolating variables into SQL query strings, which we are told is a no-no for its SQL injection risk.
The solution is to use filtering and whitelisting to make sure that the content doesn't contain some unsafe content. For example, if it's a dynamic table name, strip out anything but characters you know you want to keep, and then also delimit the table name just in case someone names their table a reserved word like "table" or "order" or something.
$table = preg_replace("/[^\w]/", "", $table);
$sql = "CREATE TABLE `{$table}` ( ... )";
Re your comment:
Yes, column names are off limits as well. As I said at the top, parameters are only for scalar values.
You also need to learn the appropriate usage of the three different types of quote marks.
What is the difference between single and double quotes in SQL?
Do different databases use different name quote?
Is it possible to use a query to swap positions of characters within the SAME value/column?
Example, I have a large table with a column that lists sizes like so:
12x24 Bulletin
7x14 Bulletin
14x48 Bulletin, etc.
The problem is now my boss has decided that he wants it to read:
Bulletin 12x24
Bulletin 7x14
Bulletin 14x48
Is it possible to swap those positions with a query or a regular expression? Or do I have to go manually update each record (very time consuming as there aprrox. 500 records to be modified)? Thanks for your help!
Is it always a whitespace between this two values? If yes, you can select your records like this:
SELECT CONCAT(SUBSTRING_INDEX(details, ' ', -1),' ',SUBSTRING_INDEX(details, ' ', 1)) AS value FROM tableName
SQL Fiddle: http://sqlfiddle.com/#!2/a04d8d/6
Table schema for this query I used:
CREATE TABLE tableName (
id int auto_increment primary key,
details varchar(30));
INSERT INTO tableName (id, details)
VALUES (NULL,'12x24 Bulletin'), (NULL,'7x14 Bulletin');
Here's what seems natural to me, though not too flexible:
UPDATE SOMETABLE
SET SOMECOLUMN = CONCAT('Bulletin ', REPLACE(SOMECOLUMN, ' Bulletin', ''))
WHERE SOMECOLUMN LIKE '% Bulletin'
;
Yes. It is possible.
The "trick" is to write an expression that returns the new value to be assigned.
It looks like you could achieve what you want by finding occurrences of ' Bulletin' and replacing that with an empty string, and pre-pending the remaining value with 'Bulletin '.
Here's a demonstration...
-- test cases
CREATE TABLE foo (id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, mycol VARCHAR(255))
INSERT INTO foo (mycol) VALUES ('12x24 Bulletin'),('7x14 Bulletin'),('14x48 Bulletin')
-- test expression to return new value to be assigned
SELECT mycol AS oldval
, CONCAT('Bulletin ',REPLACE(mycol,' Bulletin','')) AS newval
FROM foo
WHERE mycol LIKE '% Bulletin%'
-- use that expression in an UPDATE statement to replace the value
UPDATE foo
SET mycol = CONCAT('Bulletin ',REPLACE(mycol,' Bulletin',''))
WHERE mycol LIKE '% Bulletin%'
There are some gotchas to be aware of in the UPDATE statement, such as referencing a column in an expression after it's assigned a value. (Not a problem in the example I gave above, but it can be in the more general case.
Note: The MySQL REGEXP operator returns a boolean, not a string function, so, no, you can't "use a regular expression" to return a replacement value. You could use REGEXP in place the LIKE operator in the examples above.
FOLLOWUP
The above assumes you want to update the values stored in a table. If you only want to do the modification at the time you pull the data, you could use an expression in the SELECT list, similar to the above.
INSERT INTO foo (mycol) VALUES ('Stapler 12')
SELECT IF(mycol LIKE '% Bulletin'
,CONCAT('Bulletin ',REPLACE(mycol,' Bulletin',''))
,mycol
) AS mycol
FROM foo
If those examples hold true, then you can do the swapping by doing:
update t
set col = ws_concat(' ', substring_index(col, ' ', -1), substring_index(col, ' ', 1)
where col like '% Bulletin';
Alternatively, you could just do this when you query on the column:
select ws_concat(' ', substring_index(col, ' ', -1), substring_index(col, ' ', 1) as size,
<other columns
from t;
However, the existence of the problem suggests that you are stuffing two types of information into a single column. You should consider having two columns -- in your case, one would contain 'Bulletin' (some sort of "sizetype" column) and another would have the dimensions. You might even want to split the dimensions into additional columns.
Actually, as I write this, I suspect that you are missing an entity in your database. This column should actually be a foreign key to a sizes table. That table would have one row per size thingee. You can then have various columns for representing the size to suite you, your boss, your boss's boss, or whoever.
I am running MySQL version 5.1.57
I have a HTML form where a user can insert a search-string. I create a $_SESSION on this string, then I run it in a MySQLquery. Something like this:
<?php
$sql = mysql_query ("SELECT
s.student_firstname, s.student_lastname
FROM students s
WHERE (
s.student_firstname LIKE '%$searchstring%' OR
s.student_lastname LIKE '%$searchstring%
)
AND s.isActive = '1' ");
?>
The problem is when a user is searching for multiple words. Then my query fails because it is trying to match the string against the values in either column.
I've read something about MySQL FULLTEXT indexing but as far as I understand, it only works on MyISAM tables(?). How can I be able to search for multiple words using the environment that I have?
I think you should split your searched string on space (" ") and insert each segment in your query, or in another query. For example :
$str = "word1 word2";
With that you search first for the whole string "word1 word2" and after you search in you database for "word1" and "word2".
With this solution you should handle a word ignore list, because words like "a, an, the, or, ..." shouldn't be seek ...
I'm not sure there is an other way with an innoDB table ... The best solution is obviously to use the "match against" command, but it's only available with a full text index under MyISAM.
What I've been trying to do is to select a row from a table while treating the varchar cells as int ones,
Here's a little explanation:
I have a table of phone numbers, some have "-" in them, some don't.
I wanted to select a number from the database, without including those "-" in the query.
So I used this preg_replace function:
$number = preg_replace("/[^0-9]/","",$number); //that leaves only the numbers in the variable
and then I run the following query:
"SELECT * FROM `contacts` WHERE `phone` = '{$number}'"
Now, of course it won't match sometimes since the number Im searching may have "-" in the database, so I tried to look for a solution,
on solution is just converting the cells into int's, but I'm not interested in doing that,
So after looking around, I found a MySQL function named CAST, used like : CAST(phone AS UNSIGNED)
I tried to mess with it, but it didn't seem to work.
Edit:
I kept looking around for a solution, and eventually used MySQL's REPLACE function for that.
"SELECT * FROM `contacts` WHERE REPLACE(phone,'-','') = '{$number}'"
Thank you all for your help.
MySQL doesn’t support extraction of regex matches.
You could try writing a stored function to handle it, but your best bet is to convert the data to ints so that all the numbers are uniform. I know you said you don't want to do that, but if you can, then it’s the best thing to do. Otherwise, you could do something like:
"SELECT * FROM `contacts` WHERE `phone` = '{$number}' OR `phone` = '{$number_with_dashes}'"
That is, search for the plain number OR the number with dashes.
1.
The easiest way to do it might be by using the REPLACE operator.
SELECT * FROM `contacts` WHERE REPLACE(REPLACE(`phone`, '-', ''), ' ', '') = '5550100';
What it does is simpy replacing all whitespaces and dashes with nothing, namely removing all spaces and dashes.
2.
Another alternative to solve the problem would be to use LIKE. If the phone numbers with a dash always are formatted the same way like 555-0100 and 555-0199 you can simple insert a %-sign instead of the dash. If your number may be formatted in different ways you can insert a %-between every character. It's not a beautiful solution but it does the trick.
SELECT * FROM `contacts` WHERE `phone` LIKE '555%0100';
or
SELECT * FROM `contacts` WHERE `phone` LIKE '5%5%5%0%1%0%0';
3.
You can use regular expressions. Since MySQL doesn't implement regex replace functions you need to use user defined functions. Have a look at https://launchpad.net/mysql-udf-regexp. It supports REGEXP_LIKE, REGEXP_SUBSTR, REGEXP_INSTR and REGEXP_REPLACE.
Edit: Removed my first answer and added some other alternatives.
I kept looking around for a solution, and eventually used MySQL's REPLACE function for that.
"SELECT * FROM `contacts` WHERE REPLACE(phone,'-','') = '{$number}'"
I'm having trouble with the sql below. Basically I have rows that contains strings according to the format: 129&c=cars. I only want the digits part, e.g. 129. The sql query is:
$result = mysql_query("SELECT * FROM " . $db_table . " WHERE id LIKE '" . $id . "%'");
Why doesn't % work? I can't use %...% because it catches too much.
I would actually recommend using regular expressions fo the matching, but unfortunately, there is no way to capture the matching part with mysql. You will have to do the extraction in php. If you have an array containing all the results called $array:
$array = preg_replace('/^(\d+).*/', '$1', $array);
You can use the MySQL 'regexp' stuff in the WHERE clause to reduce the amount of data retrieved to just the rows you want. The basic for of your query would look like:
SELECT * FROM table WHERE field REGEXP '^$id&'
where $id is inserted by PHP and the data you want is always at the start of the field and followed by a &. If not, adjust the regex to suit, of course.
MySQL's regex engine can't do capturing, unfortunately, so you'll still have to do some parsing in PHP as soulmerge showed above, but with the 'where regexp' stuff in MySQL, you'll only have to deal with rows you know contain the data you want, not the entire table.
Using a query like this:
SELECT *
FROM mytable
WHERE id >= '0' COLLATE UTF8_BIN
AND id < ':' COLLATE UTF8_BIN
will return all strings that start with a digit and make your expression sargable, i. e. and index on id can be used.
This will make your query run faster.