Find multiple needles in FIND_IN_SET - php

I have a variable that may contain a comma separated list. In my database, I also have a column with a column separated list.
I know I can find a single value in that db column by using FIND_IN_SET(needle, haystack)
However, if my variable contains a list such as "a,b,c" how can I check if at least one item in the list matches at least one item in the column? Is this possible?

SELECT `column_A` REGEXP CONCAT('(,|^)(', REPLACE( `column_B` ,',','|'), ')(,|$)');
SELECT '123,456,789' REGEXP CONCAT('(,|^)(', REPLACE( '1234,456,6789' ,',','|'), ')(,|$)');
This is my solution.

It sounds like you probably need some linking tables in your database. Storing comma separated lists in columns to compare against other comma separated lists is going to hurt your performance if you get to any sort of scale.
I'd highly suggest you read more about linking tables (associative entities) to perhaps convince you to change your database design somewhat:
https://en.wikipedia.org/wiki/Associative_entity
To answer your question about how you would use FIND_IN_SET to perform multiple searches in a single query, you would need to build your query dynamically.
Here is a basic example to simply show you how to build a query dynamically. Please take proper steps to prevent SQL injection (http://php.net/manual/en/security.database.sql-injection.php).
// This is the list you want to search against - for your actual implementation you would use your column name
$haystack_str = '2,4,6,8,10';
// This is the comma separated list you want to use as your needle
$search_str = '1,2,3';
// Break the string apart into separate values
$search_array = explode(',', $search_str);
// Store your query fragments
$query_array = array();
// Loop through each string value you want to use as your needle
foreach ($search_array as $needle) {
// PLEASE TAKE PRECAUTIONS AGAINST SQL INJECTION !!!
$query_array[] = sprintf('FIND_IN_SET("%s","%s")', $needle, $haystack_str);
}
// Join all pieces together using OR
$query_str = implode(' OR ', $query_array);
// Show your example query
echo 'SELECT ' . $query_str . ';';
Example: https://eval.in/867963
This produces the following query:
SELECT FIND_IN_SET("1","2,4,6,8,10") OR FIND_IN_SET("2","2,4,6,8,10") OR FIND_IN_SET("3","2,4,6,8,10");
Returns a value in the range of 1 to N if the string str is in the string list strlist consisting of N substrings.
Returns 0 if str is not in strlist or if strlist is the empty string.
https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_find-in-set
The sample query will produce 1 which indicates one of your search values is in your set.

Related

Sql query to extract data containing a specific text with inserted 0 in any position

Suppose I have a variable
$sub='MCS1';
Now I want to run a query to extract data from the column Subject say that contains the following elements
0MCS1, MCS01, MCS001, MCS10, MCS001, MCS010
I have tried using like but could not reach the goal as the position of 0 or 00 can be anywhere in the string.
Please give me a clue.
A clue of mine would be to use REGEXP: https://dev.mysql.com/doc/refman/8.0/en/pattern-matching.html
You would have to dissect your base string into parts, though. E.g. $prefix = 'MCS'; $suffix = '1';
In your case, the regexp could look like: '.*'.$prefix.'{1}[0]*['.$suffix.']{1}.*'
SELECT *
FROM table
WHERE Subject REGEXP '.*MCS{1}[0]*[1]{1}.*';

Mysql FullText index searching issue

I am using MySql FullText indexing to search data from database.
Here is the query
$search_input_text = 'the_string_to_be_search';
$searchArray = explode(" ", $search_input_text);
$query="SELECT * FROM car_details
WHERE MATCH (car_trim) AGAINST ('";
foreach ($searchArray as $word) {
$query .= "+".$word."* ";
}
$query .= "' IN BOOLEAN MODE) LIMIT $start, $limit";
The query is executing fine but it has a bug, if you look at the column name you will find car_trim which is inside the MATCH() function. The column has only 3 different types of values in the database which are 'T5', 'T6' and 'T5 premier'.
When I type 'Premier' in the search bar and hit Enter, it fetches the results whose values contain the word 'Premier'. But when I type T5 or T6 , it returns an empty record. Please be sure that there are lots of records with car_trim='T5', car_trim='T6' or car_trim='T5 Premier'
I am not getting that what can be the problem with the strings T5 and T6.
MySQL has two key parameters when using full text search (and a few other important ones). The key parameters are the minimum word length and the stop words list. In short, MySQL ignores words that are less than 3 or 4 characters (depending on the storage engine) or that are in the stop word list.
Your examples ("T5" and "T6") are too short -- based on the parameter defaults.
Some other configuration parameters might be of interest, such as the maximum word length and the characters that are valid for words.
You can change the parameters for full text indexing and re-build the index.
Here is a good place to start in understanding this.

MySQL IN clause - String and INT comparison

I have a stored procedure which takes in a single String parameter - the value passed into this parameter is a comma separated list of ID's from PHP - something like 2,3,4,5
`DECLARE tags_in VARCHAR(255);`
Within the Stored procedure I would like to select the rows which have ids corresponding to the ids in the parameter - the query would be like
`SELECT * from tags WHERE tag_id IN (tags_in)`
I pass in the values from PHP to MySQL using the following statement binding the value as a string
`$stmt->bindParam(':tags', '2,3,4', PDO::PARAM_STR);`
Problem - the actual query being executed by MySQL is as below - where the parameters passed in are considered as one string
`SELECT * from tags WHERE tag_id IN ('2,3,4')`
When the query I want executed is as below where the parameters are considered as individual integers
`SELECT * from tags WHERE tag_id IN (2,3,4)`
Any suggestions on I can accomplish this?
SQL placeholders can represent only SINGLE values. If you pass in some comma separated values, they won't be seen as multiple individual values with commas, they'll just be treated like a monolithic string.
e.g.
... WHERE foo IN (:bar)
... WHERE foo = :bar
are functionally identical as far as the SQL parser are concerned, and it won't make allowances for passing in your CSV values. Both will execute the same way:
... WHERE foo IN ('1,2,3')
... WHERE foo = '1,2,3'
You'll either have to limit yourself to only as many values as you have placeholders, or dynamically build your SQL and put in a placeholder for each individual value you're trying to put into the IN clause.
e.g.
$placeholders = array_fill(0, count($values_to_check) -1, '?');
$in_clause = implode(',', $placeholders);
/// builds ?,?,?,?,?,....?
$sql = "SELECT ... WHERE foo IN ($in_clause)";
$stmt = $dbh->prepare($sql);
$stmt->execute($values_to_check);
This is one place where prepared statements fall flat on their faces, and you have to fall back to good old "build some sql dynamically".
There is sometimes another way to accomplish the desired result by casting the integer you're trying to compare as a string surrounded by commas and checking if the result is contained in your list of possible values (with added commas on either side as well). It's not the most efficient for performance maybe, but it allows you to do what you want in a single procedure or query.
For example (in your case) something like this might work:
SELECT * from tags WHERE INSTR (CONCAT(',', tags_in, ','), CONCAT(',', tag_id, ',') );
MySql is a little bit weird in that it does the conversion from int to char within the CONCAT function, some other databases require explicit casting.

PHP implode function explanation

I am relatively new to PHP. The textbook im working from covers PHP5.2 and mentions nothing of implode. The information im getting from PHP manual is a bit unclear to me. Im looking for a short clear explanation and example on implode
What exactly is its purpose?
Why would you use it?
Is it secure?
I know implode returns a string of the elements of its array but how is the following 2 exmples different, and why would you use one over the other:
example 1
$query = 'SELECT `name`, `position`
FROM `player_info`
WHERE `player_id` IN (' . implode(',', $player_ids) . ')';
example 2
$result2 = mysql_query("SELECT `fixture_id`, `opponents`
FROM `fixtures` ") or die (mysql_error());
Thank you
I'm going to ignore the fact that you're using mysql_query() in your code - as that is a more vulnerable library in the PHP reportoire. When you get more comfortable, or if you can right away, use PDO.
To understand implode() you need to understand arrays(). I'm going with the assumption that you know what arrays are.
In SQL, when you use IN() - it's equivalent to multiple "OR"s.
e.g.
SELECT * FROM table t WHERE t.id IN ( 1,2,3 )
is essentially the same as:
SELECT * FROM table t WHERE t.id = '1' OR t.id = '2' OR t.id = '3' OR t.id = '3'
When you have an array in PHP - it will look like this:
$array = array ( 1 , 2 , 3 );
If you wanted to dump it into the SQL statement, it'll fail, because a query like:
SELECT * FROM table t WHERE t.id IN ( $array )
would output:
SELECT * FROM table t WHERE t.id IN ( Array )
What you want are the values within the array. That's where implode() comes in. Implode would create delimiter - which is equivalent to a consistent value that goes between each value in your array (in your example, a comma) and will output the necessary string that you need in SQL.
$array = array ( 1 , 2 , 3 );
$query = "SELECT * FROM table t WHERE t.id IN ( ".implode("," , $array)." ) ";
Is the same as:
$query = "SELECT * FROM table t WHERE t.id IN ( 1,2,3 ) ";
Hope that helps
Implode can be particularly useful for lists. Say you have an array of ingredients, you might echo implode("\n",$ingredients) to show them on their own lines.
In your first example, you need to be very careful. It will only work properly if $player_ids is an array of numbers. implode() by itself is perfectly secure, however improper use can leave gaping holes in your security. A more "correct" version of that code might look like this:
$query = "SELECT `name`, `position` FROM `player_info`
WHERE `player_id` IN ('".implode("','",array_map("mysql_real_escape_string",$player_ids))."')";
The above code also handles the case where $player_ids is empty. In your code, it will result in a MySQL syntax error (IN () is not valid), whereas in this code it will simply look for IN (''), which will match no rows.
Another use for implode might be when packing numbers. Say you have an array of digits, you might implode("",$digits) to pack them into a single string (and str_split($string) to unpack them).
In short, there are many potential uses for this function, and generally it will be obvious when the function applies. Try not to think too hard about it ;)
Example 1 extracts players information of the players who's ID's are $player_ids. Second example extracts two columns without WHERE condition.
What is your question exactly? As long as you are 100% sure the $player_ids is an array of integers! then it is safe.
Implode will take an array and transform it in a string where each element of original array will be separated by "first parameter" of implode function.
What do you mean with "secure"? I don't understand this question but I suppose that you are asking it because you seen implode() used into a db query (in the example). implode() has nothing to do with db query so no sql injection (*) and so on.
We should use it instead of loop over the whole array
(*) obviously you should pay attention of what array to implode is
implode creates a string from an array.
You must have some basic understanding of array, before you start working with it.
Thus, if you have a set of same-type data you store it in arrays. The first parameter in array is a delimiter. Second - is the array.
$animals = ("cats", "dogs", "cows");
echo implode(" &", $animals) . " are animals" ;
Will produce:
cats & dogs & cows are animals
In your first example, there is IN construction which can accept several parameters. Arrays are just suitable to work with it.
$player_ids = array(1,2,3);
"IN (".implode(", ", $player_ids).")" will result in IN (1, 2, 3)

Using MySQL "in" clause with a long string

I'm working with an application that needs to make a where query to the database to return a rows with a "t1.address_city" (a column in the database) that is equal to any of the locations in an array. I understand I should be using implode() to make this into one big string, but this doesn't return any rows - presumably because it is looking for rows that match the big string (which of course isn't just one city, it is a list). Code:
$_GET["places"] contains something like this:
["madrid", "lisbon", "london", "paris", "new york"]
When the array name, $places, is put into the where clause like IN ('$places'), I get no rows. Following the advice of other similar questions on here, I imploded the array so that it looks like "madrid, lisbon, london, paris, new york" and then use the imploded variable like IN ('$implodedplaces') but as mentioned earlier, it doesn't find any rows.
I have found that entering each place name in quotes (IN ('madrid','lisbon','london')) separately works perfectly, but of course I need the IN clause to use the $_GET["places"] since this is manipulated by the user. Is there any way to enter all of the values in $_GET["places"] as separate strings with commas in between?
Any help appreciated - feel free to ask questions if my explanation wasn't great.
You need to keep in mind that you need to do escaping:
$in = [];
foreach ($_GET["places"] as $place) {
$in[] = mysql*_real_escape_string($place);
}
$in = '"' . implode('", "', $in) . '"';
$query = "select * from t where f IN (" . $in . ")";
Additionally you need to check count($_GET["places"]) because if it's empty you query will have IN() which is a syntax error.
Watch out with IN, in most cases it requires a full table scan, and this kills mysql performance ..
You should check a query with EXPLAIN it see it.
Try
$implodedplaces = implode("','", $places);
Note that this does not escape any quotes that may be in $places.
If $_GET["places"] is a string you could try:
$implodedplaces = str_replace('"', "'", str_replace('[',"'", str_replace(']',"'",$_GET["places"])));
This does make the assumption however that you won't find quotes and brackets in your place names.

Categories